Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: off grid support #63

Merged
merged 2 commits into from
May 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,23 @@ I recommend looking at the [Example usage section](#example-usage) to understand

#### Entities object

All entites (except _battery_charge_) should have a `unit_of_measurement` attribute of W(watts) or kW(kilowatts).
At least one of _grid_, _battery_, or _solar_ is required. All entites (except _battery_charge_) should have a `unit_of_measurement` attribute of W(watts) or kW(kilowatts).

| Name | Type | Default | Description |
| -------------- | :------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| grid | `string` / `object` | **required** | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery | `string` / `object` | | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery_charge | `string` | | Entity ID providing a state with the current percentage of charge on the battery. |
| solar | `string` | | Entity ID providing a state with the value of generation. |
| Name | Type | Description |
| -------------- | :------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| grid | `string` / `object` | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery | `string` / `object` | Entity ID of a sensor supporting a single state with negative values for production and positive values for consumption or an object for [split entites](#split-entities). Examples of both can be found below. |
| battery_charge | `string` | Entity ID providing a state with the current percentage of charge on the battery. |
| solar | `string` | Entity ID providing a state with the value of generation. |

#### Split entities

Can be use with either Grid or Battery configuration. The same `unit_of_measurement` rule as above applies.

| Name | Type | Default | Description |
| ----------- | -------- | --------------------- | ------------------------------------------------------------------------------------------------- |
| consumption | `string` | **required** for grid | Entity ID providing a state value for consumption, this is required if using a split grid object. |
| production | `string` | | Entity ID providing a state value for production |
| Name | Type | Description |
| ----------- | -------- | ------------------------------------------------------------------------------------------------- |
| consumption | `string` | Entity ID providing a state value for consumption, this is required if using a split grid object. |
| production | `string` | Entity ID providing a state value for production |

### Example usage

Expand Down
197 changes: 108 additions & 89 deletions src/power-flow-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ export class PowerFlowCard extends LitElement {
@query("#solar-home-flow") solarToHomeFlow?: SVGSVGElement;

setConfig(config: PowerFlowCardConfig): void {
if (
!config.entities ||
(!config.entities.battery &&
!config.entities.grid &&
!config.entities.solar)
) {
throw new Error(
"At least one entity for battery, grid or solar must be defined"
);
}
this._config = {
...config,
inverted_entities: coerceStringArray(config.inverted_entities, ","),
Expand Down Expand Up @@ -112,20 +122,26 @@ export class PowerFlowCard extends LitElement {

const { entities } = this._config;

const hasGrid = entities.grid !== undefined;

const hasBattery = entities.battery !== undefined;
const hasSolarProduction = entities.solar !== undefined;
const hasReturnToGrid =
typeof entities.grid === "string" || entities.grid.production;
hasGrid &&
(typeof entities.grid === "string" || entities.grid.production);

let totalFromGrid = 0;
if (typeof entities.grid === "string") {
if (this.entityInverted("grid"))
totalFromGrid = Math.abs(
Math.min(this.getEntityStateWatts(entities.grid), 0)
);
else totalFromGrid = Math.max(this.getEntityStateWatts(entities.grid), 0);
} else {
totalFromGrid = this.getEntityStateWatts(entities.grid.consumption);
if (hasGrid) {
if (typeof entities.grid === "string") {
if (this.entityInverted("grid"))
totalFromGrid = Math.abs(
Math.min(this.getEntityStateWatts(entities.grid), 0)
);
else
totalFromGrid = Math.max(this.getEntityStateWatts(entities.grid), 0);
} else {
totalFromGrid = this.getEntityStateWatts(entities.grid.consumption);
}
}

let totalSolarProduction: number = 0;
Expand Down Expand Up @@ -208,7 +224,6 @@ export class PowerFlowCard extends LitElement {
batteryToGrid = returnedToGrid;
}

//! Clean up this name
let solarToGrid = 0;
if (hasSolarProduction && returnedToGrid)
solarToGrid = returnedToGrid - (batteryToGrid ?? 0);
Expand Down Expand Up @@ -325,92 +340,88 @@ export class PowerFlowCard extends LitElement {
</div>`
: html``}
<div class="row">
<div class="circle-container grid">
<div class="circle">
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
${returnedToGrid !== null
? html`<span class="return">
${hasGrid
? html` <div class="circle-container grid">
<div class="circle">
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
${returnedToGrid !== null
? html`<span class="return">
<ha-svg-icon
class="small"
.path=${mdiArrowLeft}
></ha-svg-icon
>${this.displayValue(returnedToGrid)}
</span>`
: null}
<span class="consumption">
<ha-svg-icon
class="small"
.path=${mdiArrowLeft}
.path=${mdiArrowRight}
></ha-svg-icon
>${this.displayValue(returnedToGrid)}
</span>`
: null}
<span class="consumption">
<ha-svg-icon
class="small"
.path=${mdiArrowRight}
></ha-svg-icon
>${this.displayValue(totalFromGrid)}
</span>
</div>
<span class="label"
>${this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_distribution.grid"
)}</span
>
</div>
>${this.displayValue(totalFromGrid)}
</span>
</div>
<span class="label"
>${this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_distribution.grid"
)}</span
>
</div>`
: html`<div class="spacer"></div>`}
<div class="circle-container home">
<div
class="circle ${classMap({
border: homeSolarCircumference === undefined,
})}"
>
<div class="circle">
<ha-svg-icon .path=${mdiHome}></ha-svg-icon>
${this.displayValue(totalHomeConsumption)}
${homeSolarCircumference !== undefined
? html`<svg>
${homeSolarCircumference !== undefined
? svg`<circle
<svg>
${homeSolarCircumference !== undefined
? svg`<circle
class="solar"
cx="40"
cy="40"
r="38"
stroke-dasharray="${homeSolarCircumference} ${
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
}"
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
}"
shape-rendering="geometricPrecision"
stroke-dashoffset="-${
CIRCLE_CIRCUMFERENCE - homeSolarCircumference
}"
/>`
: ""}
${homeBatteryCircumference
? svg`<circle
: ""}
${homeBatteryCircumference
? svg`<circle
class="battery"
cx="40"
cy="40"
r="38"
stroke-dasharray="${homeBatteryCircumference} ${
CIRCLE_CIRCUMFERENCE - homeBatteryCircumference
}"
CIRCLE_CIRCUMFERENCE - homeBatteryCircumference
}"
stroke-dashoffset="-${
CIRCLE_CIRCUMFERENCE -
homeBatteryCircumference -
(homeSolarCircumference || 0)
}"
shape-rendering="geometricPrecision"
/>`
: ""}
<circle
class="grid"
cx="40"
cy="40"
r="38"
stroke-dasharray="${homeGridCircumference ??
CIRCLE_CIRCUMFERENCE -
homeSolarCircumference! -
(homeBatteryCircumference ||
0)} ${homeGridCircumference !== undefined
? CIRCLE_CIRCUMFERENCE - homeGridCircumference
: homeSolarCircumference! +
(homeBatteryCircumference || 0)}"
stroke-dashoffset="0"
shape-rendering="geometricPrecision"
/>
</svg>`
: ""}
: ""}
<circle
class="grid"
cx="40"
cy="40"
r="38"
stroke-dasharray="${homeGridCircumference ??
CIRCLE_CIRCUMFERENCE -
homeSolarCircumference! -
(homeBatteryCircumference ||
0)} ${homeGridCircumference !== undefined
? CIRCLE_CIRCUMFERENCE - homeGridCircumference
: homeSolarCircumference! +
(homeBatteryCircumference || 0)}"
stroke-dashoffset="0"
shape-rendering="geometricPrecision"
/>
</svg>
</div>
<span class="label"
>${this.hass.localize(
Expand Down Expand Up @@ -468,7 +479,9 @@ export class PowerFlowCard extends LitElement {
<path
id="solar"
class="solar"
d="M${hasBattery ? 55 : 53},0 v15 c0,${hasBattery
d="M${hasBattery ? 55 : 53},0 v${hasGrid
? 15
: 17} c0,${hasBattery
? "35 10,30 30,30"
: "40 10,35 30,35"} h25"
vector-effect="non-scaling-stroke"
Expand Down Expand Up @@ -557,21 +570,26 @@ export class PowerFlowCard extends LitElement {
</svg>
</div>`
: ""}
<div class="lines ${classMap({ battery: hasBattery })}">
<svg
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid slice"
id="grid-home-flow"
>
<path
class="grid"
id="grid"
d="M0,${hasBattery ? 50 : hasSolarProduction ? 56 : 53} H100"
vector-effect="non-scaling-stroke"
></path>
${gridConsumption
? svg`<circle
${hasGrid
? html`<div class="lines ${classMap({ battery: hasBattery })}">
<svg
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid slice"
id="grid-home-flow"
>
<path
class="grid"
id="grid"
d="M0,${hasBattery
? 50
: hasSolarProduction
? 56
: 53} H100"
vector-effect="non-scaling-stroke"
></path>
${gridConsumption
? svg`<circle
r="1"
class="grid"
vector-effect="non-scaling-stroke"
Expand All @@ -584,9 +602,10 @@ export class PowerFlowCard extends LitElement {
<mpath xlink:href="#grid" />
</animateMotion>
</circle>`
: ""}
</svg>
</div>
: ""}
</svg>
</div>`
: null}
${hasBattery
? html`<div class="lines ${classMap({ battery: hasBattery })}">
<svg
Expand All @@ -598,7 +617,7 @@ export class PowerFlowCard extends LitElement {
<path
id="battery-home"
class="battery-home"
d="M55,100 v-15 c0,-35 10,-30 30,-30 h20"
d="M55,100 v-${hasGrid ? 15 : 17} c0,-35 10,-30 30,-30 h20"
vector-effect="non-scaling-stroke"
></path>
${batteryConsumption
Expand All @@ -619,7 +638,7 @@ export class PowerFlowCard extends LitElement {
</svg>
</div>`
: ""}
${hasBattery
${hasGrid && hasBattery
? html`<div class="lines ${classMap({ battery: hasBattery })}">
<svg
viewBox="0 0 100 100"
Expand Down