mirror of
https://github.com/home-assistant/frontend.git
synced 2026-07-07 16:03:15 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f86d4c855 | |||
| e5c8c4939a | |||
| 8f4eb4e331 | |||
| d29ef73162 | |||
| 85f22d5103 | |||
| 2f0fc6b01e | |||
| 32c90fe410 | |||
| a7a182059f | |||
| d2d03381d8 | |||
| b3972eea33 | |||
| 89c3c42d66 |
@@ -4,6 +4,7 @@ import type {
|
||||
GridSourceTypeEnergyPreference,
|
||||
} from "../../../data/energy";
|
||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
|
||||
/** Strategy config shared by the per-view energy strategies. */
|
||||
export interface EnergyViewStrategyConfig extends LovelaceStrategyConfig {
|
||||
@@ -36,6 +37,22 @@ export const hasReturn = (prefs: EnergyPreferences): boolean =>
|
||||
export const hasSolar = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "solar");
|
||||
|
||||
/** Solar source with a live power sensor (`stat_rate`); the "now" scene card shows live power only. */
|
||||
export const hasSolarPower = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some(
|
||||
(source) => source.type === "solar" && !!source.stat_rate
|
||||
);
|
||||
|
||||
/** A home location is configured when both coordinates are finite and not the meaningless (0, 0) origin. */
|
||||
export const hasLocation = (hass: HomeAssistant): boolean => {
|
||||
const { latitude, longitude } = hass.config;
|
||||
return (
|
||||
Number.isFinite(latitude) &&
|
||||
Number.isFinite(longitude) &&
|
||||
!(latitude === 0 && longitude === 0)
|
||||
);
|
||||
};
|
||||
|
||||
export const hasBattery = (prefs: EnergyPreferences): boolean =>
|
||||
prefs.energy_sources.some((source) => source.type === "battery");
|
||||
|
||||
@@ -262,6 +279,12 @@ export const ENERGY_CARD_CATALOG: readonly EnergyCardCatalogEntry[] = [
|
||||
),
|
||||
|
||||
// --- Now (power) ---
|
||||
entry(
|
||||
"now",
|
||||
"energy-solar-scene-now",
|
||||
"ui.panel.energy.cards.energy_solar_scene_title",
|
||||
(p) => hasSolarPower(p)
|
||||
),
|
||||
entry(
|
||||
"now",
|
||||
"power-sources-graph",
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { EnergyViewStrategyConfig } from "./energy-cards";
|
||||
import {
|
||||
hasGasRateSource,
|
||||
hasLocation,
|
||||
hasPowerDevices,
|
||||
hasPowerSources,
|
||||
hasWaterRateDevices,
|
||||
@@ -71,6 +72,21 @@ export class PowerViewStrategy extends ReactiveElement {
|
||||
return view;
|
||||
}
|
||||
|
||||
// Solar scene (snapshot): a live, present-instant 3D view, pinned to the top of the Now tab. Needs a home
|
||||
// location to place the house, so it is skipped entirely when none is configured.
|
||||
if (
|
||||
hasLocation(hass) &&
|
||||
isEnergyCardVisible("now", "energy-solar-scene-now", prefs, hidden)
|
||||
) {
|
||||
chartsSection.cards!.push({
|
||||
type: "energy-solar-scene-now",
|
||||
collection_key: collectionKey,
|
||||
grid_options: {
|
||||
columns: 36,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (hasPowerSrc) {
|
||||
badges.push({
|
||||
type: "power-total",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,8 @@ const LAZY_LOAD_TYPES = {
|
||||
import("../cards/energy/hui-energy-devices-detail-graph-card"),
|
||||
"energy-distribution": () =>
|
||||
import("../cards/energy/hui-energy-distribution-card"),
|
||||
"energy-solar-scene-now": () =>
|
||||
import("../cards/energy/hui-energy-solar-scene-now-card"),
|
||||
"energy-gas-graph": () => import("../cards/energy/hui-energy-gas-graph-card"),
|
||||
"energy-water-graph": () =>
|
||||
import("../cards/energy/hui-energy-water-graph-card"),
|
||||
|
||||
@@ -11297,6 +11297,16 @@
|
||||
"energy_gas_graph_title": "Gas consumption",
|
||||
"energy_water_graph_title": "Water consumption",
|
||||
"energy_distribution_title": "Energy distribution",
|
||||
"energy_solar_scene_title": "Solar scene",
|
||||
"energy_solar_scene_a11y": {
|
||||
"solar": "solar production {value}",
|
||||
"home": "home consumption {value}",
|
||||
"grid_import": "grid import {value}",
|
||||
"grid_export": "grid export {value}",
|
||||
"battery_charge": "battery charging {value}",
|
||||
"battery_discharge": "battery discharging {value}",
|
||||
"battery_soc": "battery charge {value}"
|
||||
},
|
||||
"energy_sources_table_title": "Sources",
|
||||
"energy_devices_graph_title": "Individual devices total usage",
|
||||
"energy_devices_detail_graph_title": "Individual devices detail usage",
|
||||
|
||||
@@ -3,12 +3,15 @@ import type {
|
||||
EnergyPreferences,
|
||||
EnergySource,
|
||||
} from "../../../../src/data/energy";
|
||||
import type { HomeAssistant } from "../../../../src/types";
|
||||
import {
|
||||
applicableEnergyCardKeys,
|
||||
ENERGY_CARD_CATALOG,
|
||||
energyCardKey,
|
||||
hasEnergySource,
|
||||
hasGasRateSource,
|
||||
hasLocation,
|
||||
hasSolarPower,
|
||||
hasWaterRateSource,
|
||||
isEnergyCardHidden,
|
||||
isEnergyCardVisible,
|
||||
@@ -225,3 +228,42 @@ describe("isEnergyCardVisible", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasSolarPower", () => {
|
||||
const SOLAR_RATE = source({
|
||||
type: "solar",
|
||||
stat_energy_from: "sensor.solar",
|
||||
stat_rate: "sensor.solar_power",
|
||||
});
|
||||
|
||||
it("requires a solar source with a live rate sensor", () => {
|
||||
expect(hasSolarPower(makePrefs({ energy_sources: [SOLAR_RATE] }))).toBe(
|
||||
true
|
||||
);
|
||||
// Solar configured for energy only (no stat_rate) -> nothing live for the "now" card.
|
||||
expect(hasSolarPower(makePrefs({ energy_sources: [SOLAR] }))).toBe(false);
|
||||
// No solar source at all.
|
||||
expect(hasSolarPower(makePrefs({ energy_sources: [GRID_RETURN] }))).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasLocation", () => {
|
||||
const hassAt = (latitude: number, longitude: number): HomeAssistant =>
|
||||
({ config: { latitude, longitude } }) as unknown as HomeAssistant;
|
||||
|
||||
it("is true for real coordinates, including negative ones", () => {
|
||||
expect(hasLocation(hassAt(52.37, 4.9))).toBe(true);
|
||||
expect(hasLocation(hassAt(-33.87, -70.66))).toBe(true);
|
||||
});
|
||||
|
||||
it("is false at the meaningless (0, 0) origin", () => {
|
||||
expect(hasLocation(hassAt(0, 0))).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when a coordinate is not finite", () => {
|
||||
expect(hasLocation(hassAt(NaN, 4.9))).toBe(false);
|
||||
expect(hasLocation(hassAt(52.37, Infinity))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user