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: support split consumption/production entities #12

Merged
merged 1 commit into from
May 3, 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
66 changes: 47 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,60 @@ I recommend looking at the [Example usage section](#example-usage) to understand

#### Card options

| Name | Type | Default | Description |
| ------------------------- | ------ | :-----: | --------------------------------------------------------------------------------------------------------------------------------------- |
| type **_(required)_** | string | | `custom:power-distribution-card`. |
| entities **_(required)_** | map | | One or more sensor entities in a list, see [entities map](#entities-map) for additional entity options. |
| min_flow_rate | number | .75 | Represents the fastest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |
| max_flow_rate | number | 6 | Represents the slowest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |

#### Entities map

| Name | Unit | Description |
| --------------------- | :--: | --------------------------------------------------------------------------------------------------- |
| grid **_(required)_** | kW | Entity providing a state with a positive value when consuming and a negative value when producting. |
| battery | kW | Entity providing a state with a positive value when charging and a negative value when discharging. |
| battery_charge | % | Entity providing a state with the current percentage of charge on the battery. |
| solar | kW | Entity providing a state with the value of generation. |
| Name | Type | Default | Description |
| ------------- | -------- | :----------: | --------------------------------------------------------------------------------------------------------------------------------------- |
| type | `string` | **required** | `custom:power-distribution-card`. |
| entities | `object` | **required** | One or more sensor entities, see [entities object](#entities-object) for additional entity options. |
| min_flow_rate | `number` | .75 | Represents the fastest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |
| max_flow_rate | `number` | 6 | Represents the slowest amount of time in seconds for a flow dot to travel from one end to the other, see [flow formula](#flow-formula). |

#### Entities object

| 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. |

#### Split entities

Can be use with either Grid or Battery configuration

| 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 |

### Example usage

#### Using combined entities for grid and battery that support positive state values for consumption and negative state values for production.

```yaml
type: custom:power-distribution-card
title: Realtime Distribution
entities:
battery: sensor.battery_in_out
battery_charge: sensor.battery_percent
grid: sensor.grid_in_out
solar: sensor.solar_out
max_flow_rate: 10
```

#### Using split entities for grid and battery where each consumption and production entity state has a positive value.

```yaml
type: custom:power-distribution-card
title: Realtime Distribution
entities:
battery: sensor.powerwall_battery_now
battery_charge: sensor.powerwall_charge
grid: sensor.powerwall_site_now
solar: sensor.powerwall_solar_now
battery:
consumption: sensor.battery_out
production: sensor.battery_in
battery_charge: sensor.battery_percent
grid:
consumption: sensor.grid_out
production: sensor.grid_in
solar: sensor.solar_out
max_flow_rate: 10
```

Expand Down
14 changes: 12 additions & 2 deletions src/power-distribution-card-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ import { LovelaceCardConfig } from "custom-card-helpers";

export interface PowerDistributionCardConfig extends LovelaceCardConfig {
entities: {
battery?: string;
battery?:
| string
| {
consumption: string;
production: string;
};
battery_charge?: string;
grid?: string;
grid:
| string
| {
consumption: string;
production?: string;
};
solar?: string;
};
min_flow_rate: number;
Expand Down
68 changes: 48 additions & 20 deletions src/power-distribution-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,36 +55,59 @@ export class PowerDistributionCard extends LitElement {
return max - (value / total) * (max - min);
};

private getEntityState = (entity: string | undefined): number | null => {
if (entity === undefined) return null;
return coerceNumber(this.hass.states[entity].state, null);
private getEntityState = (entity: string | undefined): number => {
if (!entity) return 0;
return coerceNumber(this.hass.states[entity].state);
};

protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}

const hasBattery = this._config.entities.battery !== undefined;
const hasSolarProduction = this._config.entities.solar !== undefined;
const { entities } = this._config;

const hasBattery = entities.battery !== undefined;
const hasSolarProduction = entities.solar !== undefined;
const hasReturnToGrid = true;

const batteryState = this.getEntityState(this._config.entities.battery);
const batteryChargeState = this.getEntityState(
this._config.entities.battery_charge
const batteryChargeState = entities.battery_charge?.length
? this.getEntityState(entities.battery_charge)
: null;
const solarState = this.getEntityState(entities.solar);

const solarToGrid = hasReturnToGrid
? roundValue(
typeof entities.grid === "string"
? Math.abs(Math.min(this.getEntityState(entities.grid), 0))
: this.getEntityState(entities.grid.production),
1
)
: 0;

const batteryToHome = roundValue(
typeof entities.battery === "string"
? Math.max(this.getEntityState(entities.battery), 0)
: this.getEntityState(entities.battery?.consumption),
1
);

const gridToHome = roundValue(
typeof entities.grid === "string"
? Math.max(this.getEntityState(entities.grid), 0)
: this.getEntityState(entities.grid.consumption),
1
);
const gridState = this.getEntityState(this._config.entities.grid);
const solarState = this.getEntityState(this._config.entities.solar);

const solarToGrid = roundValue(Math.abs(Math.min(gridState ?? 0, 0)), 1);
const batteryToHome = roundValue(Math.max(batteryState ?? 0, 0), 1);
const gridToHome = roundValue(Math.max(gridState ?? 0, 0), 1);
const solarToBattery = roundValue(
Math.abs(Math.min(batteryState ?? 0, 0)),
typeof entities.battery === "string"
? Math.abs(Math.min(this.getEntityState(entities.battery), 0))
: this.getEntityState(entities.battery?.production),
1
);

const solarToHome =
roundValue(solarState ?? 0, 1) - solarToGrid - solarToBattery;
roundValue(solarState, 1) - solarToGrid - solarToBattery;

const homeConsumption = batteryToHome + gridToHome + solarToHome;
const totalConsumption = homeConsumption + solarToBattery + solarToGrid;
Expand Down Expand Up @@ -166,10 +189,15 @@ export class PowerDistributionCard extends LitElement {
<div class="circle-container grid">
<div class="circle">
<ha-svg-icon .path=${mdiTransmissionTower}></ha-svg-icon>
<span class="return">
<ha-svg-icon class="small" .path=${mdiArrowLeft}></ha-svg-icon
>${roundValue(solarToGrid, 1)} kW
</span>
${hasReturnToGrid
? html`<span class="return">
<ha-svg-icon
class="small"
.path=${mdiArrowLeft}
></ha-svg-icon
>${roundValue(solarToGrid, 1)} kW
</span>`
: null}
<span class="consumption">
<ha-svg-icon
class="small"
Expand Down Expand Up @@ -258,7 +286,7 @@ export class PowerDistributionCard extends LitElement {
<div class="circle-container battery">
<div class="circle">
${batteryChargeState !== null
? html`<span>
? html` <span>
${formatNumber(batteryChargeState, this.hass.locale, {
maximumFractionDigits: 0,
minimumFractionDigits: 0,
Expand Down