Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/wfc-forecast-chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export class WfcForecastChart extends LitElement {
<div class="wfc-forecast-slot">
<wfc-forecast-info
.hass=${this.hass}
.weatherEntity=${this.weatherEntity}
.forecast=${item}
.config=${this.config}
.hidePrecipitation=${true}
Expand Down
16 changes: 5 additions & 11 deletions src/components/wfc-forecast-info.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { html, LitElement, nothing, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ForecastAttribute } from "../data/weather";
import { ForecastAttribute, WeatherEntity } from "../data/weather";
import { ExtendedHomeAssistant, WeatherForecastCardConfig } from "../types";
import { logger } from "../logger";
import "./wfc-wind-indicator";

@customElement("wfc-forecast-info")
export class WfcForecastInfo extends LitElement {
@property({ attribute: false }) hass!: ExtendedHomeAssistant;
@property({ attribute: false }) weatherEntity!: WeatherEntity;
@property({ attribute: false }) forecast!: ForecastAttribute;
@property({ attribute: false }) config!: WeatherForecastCardConfig;

Expand Down Expand Up @@ -46,17 +47,10 @@ export class WfcForecastInfo extends LitElement {
`
: null;
} else if (attribute === "wind_bearing" || attribute === "wind_direction") {
const windSpeed = this.forecast.wind_speed || 0;
const windBearing = this.forecast.wind_bearing || 0;
const windUnit =
this.hass.states[this.config.entity]?.attributes?.wind_speed_unit ||
this.hass.config?.unit_system?.wind_speed ||
"m/s";

return html`<wfc-wind-indicator
.windBearing="${windBearing}"
.windSpeed="${windSpeed}"
.windUnit="${windUnit}"
.hass=${this.hass}
.weatherEntity=${this.weatherEntity}
.forecast=${this.forecast}
.type="${attribute === "wind_direction" ? "direction" : "bearing"}"
></wfc-wind-indicator>`;
} else {
Expand Down
10 changes: 7 additions & 3 deletions src/components/wfc-forecast-simple.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { HomeAssistant } from "custom-card-helpers";
import { html, LitElement, nothing, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ActionHandlerEvent, fireEvent } from "custom-card-helpers";
import { actionHandler } from "../hass";
import { ForecastActionDetails, WeatherForecastCardConfig } from "../types";
import {
ExtendedHomeAssistant,
ForecastActionDetails,
WeatherForecastCardConfig,
} from "../types";
import { formatDay } from "../helpers";
import {
ForecastAttribute,
Expand All @@ -19,7 +22,7 @@ import "./wfc-forecast-info";

@customElement("wfc-forecast-simple")
export class WfcForecastSimple extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
@property({ attribute: false }) hass!: ExtendedHomeAssistant;
@property({ attribute: false }) weatherEntity!: WeatherEntity;
@property({ attribute: false }) forecast: ForecastAttribute[] = [];
@property({ attribute: false }) forecastType!: ForecastType;
Expand Down Expand Up @@ -83,6 +86,7 @@ export class WfcForecastSimple extends LitElement {
></wfc-forecast-details>
<wfc-forecast-info
.hass=${this.hass}
.weatherEntity=${this.weatherEntity}
.forecast=${forecast}
.config=${this.config}
></wfc-forecast-info>
Expand Down
112 changes: 54 additions & 58 deletions src/components/wfc-wind-indicator.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,108 @@
import { LitElement, html, css } from "lit";
import { LitElement, html, css, TemplateResult, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ExtendedHomeAssistant } from "../types";
import {
ForecastAttribute,
getNormalizedWindSpeed,
WeatherEntity,
} from "../data/weather";

@customElement("wfc-wind-indicator")
export class WfcWindIndicator extends LitElement {
@property({ attribute: false }) windBearing = 0;
@property({ attribute: false }) windSpeed = 0;
@property({ attribute: false }) windUnit = "m/s";
@property({ attribute: false }) hass!: ExtendedHomeAssistant;
@property({ attribute: false }) weatherEntity!: WeatherEntity;
@property({ attribute: false }) forecast!: ForecastAttribute;
@property({ attribute: false }) size = 35;
@property({ attribute: false }) radius = 15;
@property({ attribute: false }) type: "bearing" | "direction" = "bearing";

static styles = css`
:host {
display: inline-block;
line-height: 0;
}
text {
font-size: calc(var(--ha-font-size-s, 12px) * 1.2);
font-size: calc(var(--ha-font-size-s, 12px) * 1.1);
fill: var(--primary-text-color);
font-weight: 500;
dominant-baseline: central;
}
`;

render() {
protected render(): TemplateResult | typeof nothing {
if (!this.hass || !this.forecast || !this.weatherEntity) {
return nothing;
}

const windSpeed = this.forecast.wind_speed || 0;
const windBearing = this.forecast.wind_bearing || 0;

const R = this.radius;
const padding = 8;
const tipOffset = 7;
const strokeWidth = 3;
const padding = 10;
const cx = R + padding;
const cy = R + padding;
const boxSize = 2 * (R + padding);

const speed = Math.round(this.windSpeed || 0);
const lineColor = computeLineColor(speed, this.windUnit);
let bearing = this.windBearing || 0;
const speed = Math.round(windSpeed);
const lineColor = this.computeLineColor();

let bearing = windBearing;
if (this.type === "direction") {
bearing = (bearing + 180) % 360;
}

const polar = (deg: number, r: number) => {
const a = (deg * Math.PI) / 180;
return {
x: cx + r * Math.cos(a),
y: cy + r * Math.sin(a),
};
};

const baseAngle = -90;
const spread = 12;
const p1 = polar(baseAngle - spread, R);
const p2 = polar(baseAngle + spread, R);
const tip = polar(baseAngle, R + tipOffset);
const baseY = cy - R;
const tipY = baseY - 8;
const spreadX = 7;

return html`
<svg
width="${this.size}"
height="${this.size}"
viewBox="0 0 ${boxSize} ${boxSize}"
role="img"
aria-label="Wind speed: ${speed}, bearing: ${windBearing} degrees"
>
<circle
cx="${cx}"
cy="${cy}"
r="${R}"
stroke="${lineColor}"
stroke-width="3"
stroke-width="${strokeWidth}"
fill="none"
/>

<g transform="rotate(${bearing} ${cx} ${cy})">
<polygon
points="
${p1.x},${p1.y}
${p2.x},${p2.y}
${tip.x},${tip.y}
${cx - spreadX},${baseY}
${cx + spreadX},${baseY}
${cx},${tipY}
"
fill="${lineColor}"
/>
</g>

<text x="${cx}" y="${cy + 5}" text-anchor="middle">${speed}</text>
<text x="${cx}" y="${cy}" text-anchor="middle">${speed}</text>
</svg>
`;
}
}

const computeLineColor = (windSpeed: number, windUnit: string): string => {
const unit = windUnit.toLowerCase();

const multipliers: Record<string, number> = {
"km/h": 1 / 3.6,
kmh: 1 / 3.6,
mph: 0.44704,
kn: 0.514444,
kt: 0.514444,
knot: 0.514444,
knots: 0.514444,
};
private computeLineColor(): string {
const speedMS = getNormalizedWindSpeed(
this.hass,
this.weatherEntity,
this.forecast
);

const multiplier = multipliers[unit] ?? 1;
const speedMS = windSpeed * multiplier;

if (speedMS <= 3) {
return "var(--wfc-wind-low)";
}

if (speedMS <= 8) {
return "var(--wfc-wind-medium)";
}

return "var(--wfc-wind-high)";
};
if (!speedMS || speedMS <= 3) {
return "var(--wfc-wind-low)";
}
if (speedMS <= 8) {
return "var(--wfc-wind-medium)";
}

declare global {
interface HTMLElementTagNameMap {
"wfc-wind-indicator": WfcWindIndicator;
return "var(--wfc-wind-high)";
}
}
Loading