fix: change solar surplus calculation
This commit is contained in:
@@ -8,11 +8,19 @@ from homeassistant.helpers import device_registry as dr, selector
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_CHARGER_ID,
|
CONF_CHARGER_ID,
|
||||||
|
CHARGING_PROFILE_ALL_SURPLUS,
|
||||||
|
CHARGING_PROFILE_CONSERVATIVE,
|
||||||
|
CONF_CHARGING_PROFILE,
|
||||||
|
CONF_DEAD_BAND_RESYNC_MINUTES,
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
|
CONF_EV_CHARGING_SENSOR,
|
||||||
|
CONF_HOUSE_LOAD_SENSOR,
|
||||||
CONF_NOTIFY_TARGET,
|
CONF_NOTIFY_TARGET,
|
||||||
CONF_SITE_ID,
|
CONF_SITE_ID,
|
||||||
CONF_SOLAR_EXCESS_SENSOR,
|
CONF_SOLAR_PRODUCTION_SENSOR,
|
||||||
|
DEFAULT_CHARGING_PROFILE,
|
||||||
CONF_STOP_GRACE_MINUTES,
|
CONF_STOP_GRACE_MINUTES,
|
||||||
|
DEFAULT_DEAD_BAND_RESYNC_MINUTES,
|
||||||
DEFAULT_STOP_GRACE_MINUTES,
|
DEFAULT_STOP_GRACE_MINUTES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
EASEE_DOMAIN,
|
EASEE_DOMAIN,
|
||||||
@@ -24,11 +32,31 @@ _STOP_GRACE_SELECTOR = selector.NumberSelector(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_DEAD_BAND_RESYNC_SELECTOR = selector.NumberSelector(
|
||||||
|
selector.NumberSelectorConfig(
|
||||||
|
min=1, max=60, step=1, unit_of_measurement="min", mode=selector.NumberSelectorMode.BOX
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_CHARGING_PROFILE_SELECTOR = selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=[
|
||||||
|
selector.SelectOptionDict(value=CHARGING_PROFILE_CONSERVATIVE, label="Conservative"),
|
||||||
|
selector.SelectOptionDict(value=CHARGING_PROFILE_ALL_SURPLUS, label="All Surplus"),
|
||||||
|
],
|
||||||
|
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
_POWER_SENSOR_SELECTOR = selector.EntitySelector(
|
||||||
|
selector.EntitySelectorConfig(domain="sensor", device_class="power")
|
||||||
|
)
|
||||||
|
|
||||||
_STATIC_USER_FIELDS = {
|
_STATIC_USER_FIELDS = {
|
||||||
vol.Required(CONF_SITE_ID): str,
|
vol.Required(CONF_SITE_ID): str,
|
||||||
vol.Required(CONF_SOLAR_EXCESS_SENSOR): selector.EntitySelector(
|
vol.Required(CONF_SOLAR_PRODUCTION_SENSOR): _POWER_SENSOR_SELECTOR,
|
||||||
selector.EntitySelectorConfig(domain="sensor")
|
vol.Required(CONF_HOUSE_LOAD_SENSOR): _POWER_SENSOR_SELECTOR,
|
||||||
),
|
vol.Required(CONF_EV_CHARGING_SENSOR): _POWER_SENSOR_SELECTOR,
|
||||||
vol.Required(CONF_DEVICE_ID): selector.DeviceSelector(
|
vol.Required(CONF_DEVICE_ID): selector.DeviceSelector(
|
||||||
selector.DeviceSelectorConfig(integration="easee")
|
selector.DeviceSelectorConfig(integration="easee")
|
||||||
),
|
),
|
||||||
@@ -63,10 +91,18 @@ def _user_schema(hass: HomeAssistant) -> vol.Schema:
|
|||||||
|
|
||||||
def _options_schema(hass: HomeAssistant, current: dict) -> vol.Schema:
|
def _options_schema(hass: HomeAssistant, current: dict) -> vol.Schema:
|
||||||
return vol.Schema({
|
return vol.Schema({
|
||||||
|
vol.Required(
|
||||||
|
CONF_CHARGING_PROFILE,
|
||||||
|
default=current.get(CONF_CHARGING_PROFILE, DEFAULT_CHARGING_PROFILE),
|
||||||
|
): _CHARGING_PROFILE_SELECTOR,
|
||||||
vol.Required(
|
vol.Required(
|
||||||
CONF_STOP_GRACE_MINUTES,
|
CONF_STOP_GRACE_MINUTES,
|
||||||
default=current.get(CONF_STOP_GRACE_MINUTES, DEFAULT_STOP_GRACE_MINUTES),
|
default=current.get(CONF_STOP_GRACE_MINUTES, DEFAULT_STOP_GRACE_MINUTES),
|
||||||
): _STOP_GRACE_SELECTOR,
|
): _STOP_GRACE_SELECTOR,
|
||||||
|
vol.Required(
|
||||||
|
CONF_DEAD_BAND_RESYNC_MINUTES,
|
||||||
|
default=current.get(CONF_DEAD_BAND_RESYNC_MINUTES, DEFAULT_DEAD_BAND_RESYNC_MINUTES),
|
||||||
|
): _DEAD_BAND_RESYNC_SELECTOR,
|
||||||
vol.Optional(CONF_NOTIFY_TARGET): _notify_selector(_notify_services(hass)),
|
vol.Optional(CONF_NOTIFY_TARGET): _notify_selector(_notify_services(hass)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ DOMAIN = "easee_solar_charging"
|
|||||||
CONF_CHARGER_ID = "charger_id"
|
CONF_CHARGER_ID = "charger_id"
|
||||||
CONF_DEVICE_ID = "device_id"
|
CONF_DEVICE_ID = "device_id"
|
||||||
CONF_SITE_ID = "site_id"
|
CONF_SITE_ID = "site_id"
|
||||||
CONF_SOLAR_EXCESS_SENSOR = "solar_excess_sensor"
|
CONF_SOLAR_PRODUCTION_SENSOR = "solar_production_sensor"
|
||||||
|
CONF_HOUSE_LOAD_SENSOR = "house_load_sensor"
|
||||||
|
CONF_EV_CHARGING_SENSOR = "ev_charging_sensor"
|
||||||
|
|
||||||
|
ROLLING_AVERAGE_SECONDS = 30
|
||||||
|
|
||||||
EASEE_DOMAIN = "easee"
|
EASEE_DOMAIN = "easee"
|
||||||
EASEE_SERVICE_SET_DYNAMIC_LIMIT = "set_charger_dynamic_limit"
|
EASEE_SERVICE_SET_DYNAMIC_LIMIT = "set_charger_dynamic_limit"
|
||||||
@@ -25,6 +29,14 @@ HYSTERESIS_A = 2 # dead-band within the charging range (fixed)
|
|||||||
CONF_STOP_GRACE_MINUTES = "stop_grace_minutes"
|
CONF_STOP_GRACE_MINUTES = "stop_grace_minutes"
|
||||||
DEFAULT_STOP_GRACE_MINUTES = 5
|
DEFAULT_STOP_GRACE_MINUTES = 5
|
||||||
|
|
||||||
|
CONF_CHARGING_PROFILE = "charging_profile"
|
||||||
|
CHARGING_PROFILE_CONSERVATIVE = "conservative"
|
||||||
|
CHARGING_PROFILE_ALL_SURPLUS = "all_surplus"
|
||||||
|
DEFAULT_CHARGING_PROFILE = CHARGING_PROFILE_CONSERVATIVE
|
||||||
|
|
||||||
|
CONF_DEAD_BAND_RESYNC_MINUTES = "dead_band_resync_minutes"
|
||||||
|
DEFAULT_DEAD_BAND_RESYNC_MINUTES = 1
|
||||||
|
|
||||||
CONF_NOTIFY_TARGET = "notify_target"
|
CONF_NOTIFY_TARGET = "notify_target"
|
||||||
|
|
||||||
# Watts available at each valid current step on a 3-phase 230V system (P = 3 × V × I)
|
# Watts available at each valid current step on a 3-phase 230V system (P = 3 × V × I)
|
||||||
|
|||||||
@@ -1,18 +1,29 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
CHARGING_PROFILE_ALL_SURPLUS,
|
||||||
|
CHARGING_PROFILE_CONSERVATIVE,
|
||||||
|
CONF_CHARGING_PROFILE,
|
||||||
CONF_CHARGER_ID,
|
CONF_CHARGER_ID,
|
||||||
|
CONF_DEAD_BAND_RESYNC_MINUTES,
|
||||||
CONF_DEVICE_ID,
|
CONF_DEVICE_ID,
|
||||||
|
CONF_EV_CHARGING_SENSOR,
|
||||||
|
CONF_HOUSE_LOAD_SENSOR,
|
||||||
CONF_NOTIFY_TARGET,
|
CONF_NOTIFY_TARGET,
|
||||||
CONF_SOLAR_EXCESS_SENSOR,
|
CONF_SOLAR_PRODUCTION_SENSOR,
|
||||||
CONF_STOP_GRACE_MINUTES,
|
CONF_STOP_GRACE_MINUTES,
|
||||||
|
DEFAULT_CHARGING_PROFILE,
|
||||||
|
DEFAULT_DEAD_BAND_RESYNC_MINUTES,
|
||||||
DEFAULT_SCAN_INTERVAL,
|
DEFAULT_SCAN_INTERVAL,
|
||||||
DEFAULT_STOP_GRACE_MINUTES,
|
DEFAULT_STOP_GRACE_MINUTES,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@@ -21,12 +32,12 @@ from .const import (
|
|||||||
EASEE_DOMAIN,
|
EASEE_DOMAIN,
|
||||||
EASEE_SERVICE_SET_DYNAMIC_LIMIT,
|
EASEE_SERVICE_SET_DYNAMIC_LIMIT,
|
||||||
HYSTERESIS_A,
|
HYSTERESIS_A,
|
||||||
|
ROLLING_AVERAGE_SECONDS,
|
||||||
solar_excess_to_charger_current,
|
solar_excess_to_charger_current,
|
||||||
)
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_DEAD_BAND_RESYNC = timedelta(minutes=1)
|
|
||||||
|
|
||||||
|
|
||||||
class EaseeSolarCoordinator(DataUpdateCoordinator):
|
class EaseeSolarCoordinator(DataUpdateCoordinator):
|
||||||
@@ -36,8 +47,15 @@ class EaseeSolarCoordinator(DataUpdateCoordinator):
|
|||||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||||
self._entry = entry
|
self._entry = entry
|
||||||
self.charger_id = entry.data[CONF_CHARGER_ID]
|
self.charger_id = entry.data[CONF_CHARGER_ID]
|
||||||
self._solar_excess_sensor = entry.data[CONF_SOLAR_EXCESS_SENSOR]
|
|
||||||
self._device_id: str = entry.data[CONF_DEVICE_ID]
|
self._device_id: str = entry.data[CONF_DEVICE_ID]
|
||||||
|
self._production_sensor = entry.data[CONF_SOLAR_PRODUCTION_SENSOR]
|
||||||
|
self._house_load_sensor = entry.data[CONF_HOUSE_LOAD_SENSOR]
|
||||||
|
self._ev_charging_sensor = entry.data[CONF_EV_CHARGING_SENSOR]
|
||||||
|
self._samples: dict[str, deque[tuple[datetime, float]]] = {
|
||||||
|
self._production_sensor: deque(),
|
||||||
|
self._house_load_sensor: deque(),
|
||||||
|
self._ev_charging_sensor: deque(),
|
||||||
|
}
|
||||||
self._last_sent_current: int | None = None
|
self._last_sent_current: int | None = None
|
||||||
self._last_sent_at: datetime | None = None
|
self._last_sent_at: datetime | None = None
|
||||||
self._below_threshold_since: datetime | None = None
|
self._below_threshold_since: datetime | None = None
|
||||||
@@ -47,25 +65,71 @@ class EaseeSolarCoordinator(DataUpdateCoordinator):
|
|||||||
name=DOMAIN,
|
name=DOMAIN,
|
||||||
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
|
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
|
||||||
)
|
)
|
||||||
|
self._setup_state_listeners()
|
||||||
|
|
||||||
|
def _setup_state_listeners(self) -> None:
|
||||||
|
@callback
|
||||||
|
def _on_state_change(event) -> None:
|
||||||
|
entity_id: str = event.data["entity_id"]
|
||||||
|
new_state = event.data.get("new_state")
|
||||||
|
if new_state is None or new_state.state in ("unknown", "unavailable"):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
value = float(new_state.state)
|
||||||
|
except ValueError:
|
||||||
|
return
|
||||||
|
samples = self._samples[entity_id]
|
||||||
|
samples.append((datetime.now(timezone.utc), value))
|
||||||
|
|
||||||
|
self._entry.async_on_unload(
|
||||||
|
async_track_state_change_event(self.hass, list(self._samples), _on_state_change)
|
||||||
|
)
|
||||||
|
|
||||||
|
def _rolling_average(self, entity_id: str) -> float | None:
|
||||||
|
now = datetime.now(timezone.utc)
|
||||||
|
cutoff = now - timedelta(seconds=ROLLING_AVERAGE_SECONDS)
|
||||||
|
samples = self._samples[entity_id]
|
||||||
|
while samples and samples[0][0] < cutoff:
|
||||||
|
samples.popleft()
|
||||||
|
if samples:
|
||||||
|
return sum(v for _, v in samples) / len(samples)
|
||||||
|
# No samples collected yet — fall back to current state
|
||||||
|
state = self.hass.states.get(entity_id)
|
||||||
|
if state is None or state.state in ("unknown", "unavailable"):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return float(state.state)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _stop_grace(self) -> timedelta:
|
def _stop_grace(self) -> timedelta:
|
||||||
minutes = self._entry.options.get(CONF_STOP_GRACE_MINUTES, DEFAULT_STOP_GRACE_MINUTES)
|
minutes = self._entry.options.get(CONF_STOP_GRACE_MINUTES, DEFAULT_STOP_GRACE_MINUTES)
|
||||||
return timedelta(minutes=minutes)
|
return timedelta(minutes=minutes)
|
||||||
|
|
||||||
def _get_solar_excess_w(self) -> float | None:
|
@property
|
||||||
state = self.hass.states.get(self._solar_excess_sensor)
|
def _dead_band_resync(self) -> timedelta:
|
||||||
if state is None or state.state in ("unknown", "unavailable"):
|
minutes = self._entry.options.get(CONF_DEAD_BAND_RESYNC_MINUTES, DEFAULT_DEAD_BAND_RESYNC_MINUTES)
|
||||||
return None
|
return timedelta(minutes=minutes)
|
||||||
try:
|
|
||||||
return float(state.state)
|
def _get_solar_excess_w(self) -> tuple[float | None, float | None, float | None, float | None]:
|
||||||
except ValueError:
|
profile = self._entry.options.get(CONF_CHARGING_PROFILE, DEFAULT_CHARGING_PROFILE)
|
||||||
_LOGGER.warning(
|
production = self._rolling_average(self._production_sensor)
|
||||||
"Solar excess sensor %s has non-numeric state: %s",
|
house_load = self._rolling_average(self._house_load_sensor) or 0.0
|
||||||
self._solar_excess_sensor,
|
ev_charging = self._rolling_average(self._ev_charging_sensor) or 0.0
|
||||||
state.state,
|
|
||||||
)
|
if production is None:
|
||||||
return None
|
return None, None, house_load, ev_charging
|
||||||
|
|
||||||
|
if profile == CHARGING_PROFILE_ALL_SURPLUS:
|
||||||
|
# Allocate all solar production to EV; house load draws from grid.
|
||||||
|
surplus = production
|
||||||
|
else:
|
||||||
|
assert profile == CHARGING_PROFILE_CONSERVATIVE
|
||||||
|
surplus = production - house_load - ev_charging
|
||||||
|
|
||||||
|
return surplus, production, house_load, ev_charging
|
||||||
|
|
||||||
|
|
||||||
def _get_charger_current_a(self) -> int | None:
|
def _get_charger_current_a(self) -> int | None:
|
||||||
entity_id = f"sensor.{self.charger_id.lower()}_current"
|
entity_id = f"sensor.{self.charger_id.lower()}_current"
|
||||||
@@ -143,7 +207,7 @@ class EaseeSolarCoordinator(DataUpdateCoordinator):
|
|||||||
# Within dead-band or same value: re-align once per minute.
|
# Within dead-band or same value: re-align once per minute.
|
||||||
return (
|
return (
|
||||||
self._last_sent_at is None
|
self._last_sent_at is None
|
||||||
or datetime.now(timezone.utc) - self._last_sent_at >= _DEAD_BAND_RESYNC
|
or datetime.now(timezone.utc) - self._last_sent_at >= self._dead_band_resync
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _async_notify(self, title: str, message: str) -> None:
|
async def _async_notify(self, title: str, message: str) -> None:
|
||||||
@@ -168,7 +232,7 @@ class EaseeSolarCoordinator(DataUpdateCoordinator):
|
|||||||
|
|
||||||
async def _async_update_data(self) -> dict:
|
async def _async_update_data(self) -> dict:
|
||||||
try:
|
try:
|
||||||
solar_excess_w = self._get_solar_excess_w()
|
solar_excess_w, production_w, house_load_w, ev_charging_w = self._get_solar_excess_w()
|
||||||
raw_target = (
|
raw_target = (
|
||||||
solar_excess_to_charger_current(solar_excess_w)
|
solar_excess_to_charger_current(solar_excess_w)
|
||||||
if self.solar_charging_enabled and solar_excess_w is not None
|
if self.solar_charging_enabled and solar_excess_w is not None
|
||||||
@@ -202,6 +266,9 @@ class EaseeSolarCoordinator(DataUpdateCoordinator):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"charger_id": self.charger_id,
|
"charger_id": self.charger_id,
|
||||||
|
"solar_production_w": production_w,
|
||||||
|
"house_load_w": house_load_w,
|
||||||
|
"ev_charging_w": ev_charging_w,
|
||||||
"solar_excess_w": solar_excess_w,
|
"solar_excess_w": solar_excess_w,
|
||||||
"target_current_a": target_current_a,
|
"target_current_a": target_current_a,
|
||||||
"charger_current_a": self._get_charger_current_a(),
|
"charger_current_a": self._get_charger_current_a(),
|
||||||
|
|||||||
@@ -24,6 +24,30 @@ class EaseeSolarSensorDescription(SensorEntityDescription):
|
|||||||
|
|
||||||
|
|
||||||
SENSOR_DESCRIPTIONS: tuple[EaseeSolarSensorDescription, ...] = (
|
SENSOR_DESCRIPTIONS: tuple[EaseeSolarSensorDescription, ...] = (
|
||||||
|
EaseeSolarSensorDescription(
|
||||||
|
key="solar_production",
|
||||||
|
data_key="solar_production_w",
|
||||||
|
name="Solar Production",
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
|
),
|
||||||
|
EaseeSolarSensorDescription(
|
||||||
|
key="house_load",
|
||||||
|
data_key="house_load_w",
|
||||||
|
name="House Load",
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
|
),
|
||||||
|
EaseeSolarSensorDescription(
|
||||||
|
key="ev_charging",
|
||||||
|
data_key="ev_charging_w",
|
||||||
|
name="EV Charging Power",
|
||||||
|
device_class=SensorDeviceClass.POWER,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
native_unit_of_measurement=UnitOfPower.WATT,
|
||||||
|
),
|
||||||
EaseeSolarSensorDescription(
|
EaseeSolarSensorDescription(
|
||||||
key="solar_excess",
|
key="solar_excess",
|
||||||
data_key="solar_excess_w",
|
data_key="solar_excess_w",
|
||||||
|
|||||||
@@ -14,11 +14,15 @@
|
|||||||
"init": {
|
"init": {
|
||||||
"title": "Easee Solar Charging options",
|
"title": "Easee Solar Charging options",
|
||||||
"data": {
|
"data": {
|
||||||
|
"charging_profile": "Charging profile",
|
||||||
"stop_grace_minutes": "Stop grace period",
|
"stop_grace_minutes": "Stop grace period",
|
||||||
|
"dead_band_resync_minutes": "Dead-band resync interval",
|
||||||
"notify_target": "Notification target"
|
"notify_target": "Notification target"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
|
"charging_profile": "Conservative: charge only on true solar surplus (production − house load − current EV draw). All Surplus: allocate all solar production to the EV; house load draws from the grid.",
|
||||||
"stop_grace_minutes": "How many minutes to keep charging after solar excess drops below the minimum threshold, before stopping the charger.",
|
"stop_grace_minutes": "How many minutes to keep charging after solar excess drops below the minimum threshold, before stopping the charger.",
|
||||||
|
"dead_band_resync_minutes": "How often to re-send the current charging level when within the dead-band, to track gradual solar drift.",
|
||||||
"notify_target": "Name of a notify service to receive start/stop alerts (e.g. mobile_app_my_phone). Leave empty to disable notifications."
|
"notify_target": "Name of a notify service to receive start/stop alerts (e.g. mobile_app_my_phone). Leave empty to disable notifications."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,12 +34,16 @@
|
|||||||
"title": "Set up Easee Solar Charging",
|
"title": "Set up Easee Solar Charging",
|
||||||
"data": {
|
"data": {
|
||||||
"site_id": "Site ID",
|
"site_id": "Site ID",
|
||||||
"solar_excess_sensor": "Solar Excess Power Sensor",
|
"solar_production_sensor": "Solar Production Sensor",
|
||||||
|
"house_load_sensor": "House Load Sensor",
|
||||||
|
"ev_charging_sensor": "EV Charging Power Sensor",
|
||||||
"device_id": "Easee Charger",
|
"device_id": "Easee Charger",
|
||||||
"notify_target": "Notification target"
|
"notify_target": "Notification target"
|
||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"solar_excess_sensor": "Sensor reporting the momentarily available solar power surplus in Watts.",
|
"solar_production_sensor": "Sensor reporting total solar panel output in Watts.",
|
||||||
|
"house_load_sensor": "Sensor reporting total household consumption (excluding EV charging) in Watts.",
|
||||||
|
"ev_charging_sensor": "Sensor reporting current EV charging power in Watts.",
|
||||||
"notify_target": "Name of a notify service to receive start/stop alerts (e.g. mobile_app_my_phone). Leave empty to disable notifications."
|
"notify_target": "Name of a notify service to receive start/stop alerts (e.g. mobile_app_my_phone). Leave empty to disable notifications."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -15,14 +15,23 @@ cards:
|
|||||||
show_icon: true
|
show_icon: true
|
||||||
show_state: true
|
show_state: true
|
||||||
entities:
|
entities:
|
||||||
- entity: sensor.esc_CHARGER_ID_solar_excess
|
- entity: sensor.esc_CHARGER_ID_solar_production
|
||||||
name: Solar Excess
|
name: Production
|
||||||
icon: mdi:solar-power
|
icon: mdi:solar-power
|
||||||
|
- entity: sensor.esc_CHARGER_ID_house_load
|
||||||
|
name: House Load
|
||||||
|
icon: mdi:home-lightning-bolt
|
||||||
|
- entity: sensor.esc_CHARGER_ID_ev_charging
|
||||||
|
name: EV Power
|
||||||
|
icon: mdi:car-electric
|
||||||
|
- entity: sensor.esc_CHARGER_ID_solar_excess
|
||||||
|
name: Surplus
|
||||||
|
icon: mdi:lightning-bolt-circle
|
||||||
- entity: sensor.esc_CHARGER_ID_target_current
|
- entity: sensor.esc_CHARGER_ID_target_current
|
||||||
name: Target Current
|
name: Target
|
||||||
icon: mdi:lightning-bolt
|
icon: mdi:lightning-bolt
|
||||||
- entity: sensor.esc_CHARGER_ID_charger_current
|
- entity: sensor.esc_CHARGER_ID_charger_current
|
||||||
name: Charger Current
|
name: Actual
|
||||||
icon: mdi:current-ac
|
icon: mdi:current-ac
|
||||||
- entity: sensor.esc_CHARGER_ID_charger_status
|
- entity: sensor.esc_CHARGER_ID_charger_status
|
||||||
name: Status
|
name: Status
|
||||||
|
|||||||
Reference in New Issue
Block a user