Add growatt battery devices (#34773)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>pull/36862/head
parent
fe03c9da68
commit
d278dd9477
|
@ -2,6 +2,6 @@
|
||||||
"domain": "growatt_server",
|
"domain": "growatt_server",
|
||||||
"name": "Growatt",
|
"name": "Growatt",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/growatt_server/",
|
"documentation": "https://www.home-assistant.io/integrations/growatt_server/",
|
||||||
"requirements": ["growattServer==0.0.1"],
|
"requirements": ["growattServer==0.0.4"],
|
||||||
"codeowners": ["@indykoning"]
|
"codeowners": ["@indykoning"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,81 +29,253 @@ DEFAULT_PLANT_ID = "0"
|
||||||
DEFAULT_NAME = "Growatt"
|
DEFAULT_NAME = "Growatt"
|
||||||
SCAN_INTERVAL = datetime.timedelta(minutes=5)
|
SCAN_INTERVAL = datetime.timedelta(minutes=5)
|
||||||
|
|
||||||
|
# Sensor type order is: Sensor name, Unit of measurement, api data name, additional options
|
||||||
|
|
||||||
TOTAL_SENSOR_TYPES = {
|
TOTAL_SENSOR_TYPES = {
|
||||||
"total_money_today": ("Total money today", "€", "plantMoneyText", None),
|
"total_money_today": ("Total money today", "€", "plantMoneyText", {}),
|
||||||
"total_money_total": ("Money lifetime", "€", "totalMoneyText", None),
|
"total_money_total": ("Money lifetime", "€", "totalMoneyText", {}),
|
||||||
"total_energy_today": (
|
"total_energy_today": ("Energy Today", ENERGY_KILO_WATT_HOUR, "todayEnergy", {},),
|
||||||
"Energy Today",
|
"total_output_power": (
|
||||||
ENERGY_KILO_WATT_HOUR,
|
"Output Power",
|
||||||
"todayEnergy",
|
POWER_WATT,
|
||||||
"power",
|
"invTodayPpv",
|
||||||
|
{"device_class": "power"},
|
||||||
),
|
),
|
||||||
"total_output_power": ("Output Power", POWER_WATT, "invTodayPpv", "power"),
|
|
||||||
"total_energy_output": (
|
"total_energy_output": (
|
||||||
"Lifetime energy output",
|
"Lifetime energy output",
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
"totalEnergy",
|
"totalEnergy",
|
||||||
"power",
|
{},
|
||||||
|
),
|
||||||
|
"total_maximum_output": (
|
||||||
|
"Maximum power",
|
||||||
|
POWER_WATT,
|
||||||
|
"nominalPower",
|
||||||
|
{"device_class": "power"},
|
||||||
),
|
),
|
||||||
"total_maximum_output": ("Maximum power", POWER_WATT, "nominalPower", "power"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INVERTER_SENSOR_TYPES = {
|
INVERTER_SENSOR_TYPES = {
|
||||||
"inverter_energy_today": (
|
"inverter_energy_today": ("Energy today", ENERGY_KILO_WATT_HOUR, "e_today", {},),
|
||||||
"Energy today",
|
|
||||||
ENERGY_KILO_WATT_HOUR,
|
|
||||||
"e_today",
|
|
||||||
"power",
|
|
||||||
),
|
|
||||||
"inverter_energy_total": (
|
"inverter_energy_total": (
|
||||||
"Lifetime energy output",
|
"Lifetime energy output",
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
"e_total",
|
"e_total",
|
||||||
"power",
|
{},
|
||||||
),
|
),
|
||||||
"inverter_voltage_input_1": ("Input 1 voltage", VOLT, "vpv1", None),
|
"inverter_voltage_input_1": ("Input 1 voltage", VOLT, "vpv1", {}),
|
||||||
"inverter_amperage_input_1": (
|
"inverter_amperage_input_1": (
|
||||||
"Input 1 Amperage",
|
"Input 1 Amperage",
|
||||||
ELECTRICAL_CURRENT_AMPERE,
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
"ipv1",
|
"ipv1",
|
||||||
None,
|
{},
|
||||||
),
|
),
|
||||||
"inverter_wattage_input_1": ("Input 1 Wattage", POWER_WATT, "ppv1", "power"),
|
"inverter_wattage_input_1": (
|
||||||
"inverter_voltage_input_2": ("Input 2 voltage", VOLT, "vpv2", None),
|
"Input 1 Wattage",
|
||||||
|
POWER_WATT,
|
||||||
|
"ppv1",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"inverter_voltage_input_2": ("Input 2 voltage", VOLT, "vpv2", {}),
|
||||||
"inverter_amperage_input_2": (
|
"inverter_amperage_input_2": (
|
||||||
"Input 2 Amperage",
|
"Input 2 Amperage",
|
||||||
ELECTRICAL_CURRENT_AMPERE,
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
"ipv2",
|
"ipv2",
|
||||||
None,
|
{},
|
||||||
),
|
),
|
||||||
"inverter_wattage_input_2": ("Input 2 Wattage", POWER_WATT, "ppv2", "power"),
|
"inverter_wattage_input_2": (
|
||||||
"inverter_voltage_input_3": ("Input 3 voltage", VOLT, "vpv3", None),
|
"Input 2 Wattage",
|
||||||
|
POWER_WATT,
|
||||||
|
"ppv2",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"inverter_voltage_input_3": ("Input 3 voltage", VOLT, "vpv3", {}),
|
||||||
"inverter_amperage_input_3": (
|
"inverter_amperage_input_3": (
|
||||||
"Input 3 Amperage",
|
"Input 3 Amperage",
|
||||||
ELECTRICAL_CURRENT_AMPERE,
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
"ipv3",
|
"ipv3",
|
||||||
None,
|
{},
|
||||||
),
|
),
|
||||||
"inverter_wattage_input_3": ("Input 3 Wattage", POWER_WATT, "ppv3", "power"),
|
"inverter_wattage_input_3": (
|
||||||
"inverter_internal_wattage": ("Internal wattage", POWER_WATT, "ppv", "power"),
|
"Input 3 Wattage",
|
||||||
"inverter_reactive_voltage": ("Reactive voltage", VOLT, "vacr", None),
|
POWER_WATT,
|
||||||
|
"ppv3",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"inverter_internal_wattage": (
|
||||||
|
"Internal wattage",
|
||||||
|
POWER_WATT,
|
||||||
|
"ppv",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"inverter_reactive_voltage": ("Reactive voltage", VOLT, "vacr", {}),
|
||||||
"inverter_inverter_reactive_amperage": (
|
"inverter_inverter_reactive_amperage": (
|
||||||
"Reactive amperage",
|
"Reactive amperage",
|
||||||
ELECTRICAL_CURRENT_AMPERE,
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
"iacr",
|
"iacr",
|
||||||
None,
|
{},
|
||||||
|
),
|
||||||
|
"inverter_frequency": ("AC frequency", FREQUENCY_HERTZ, "fac", {}),
|
||||||
|
"inverter_current_wattage": (
|
||||||
|
"Output power",
|
||||||
|
POWER_WATT,
|
||||||
|
"pac",
|
||||||
|
{"device_class": "power"},
|
||||||
),
|
),
|
||||||
"inverter_frequency": ("AC frequency", FREQUENCY_HERTZ, "fac", None),
|
|
||||||
"inverter_current_wattage": ("Output power", POWER_WATT, "pac", "power"),
|
|
||||||
"inverter_current_reactive_wattage": (
|
"inverter_current_reactive_wattage": (
|
||||||
"Reactive wattage",
|
"Reactive wattage",
|
||||||
POWER_WATT,
|
POWER_WATT,
|
||||||
"pacr",
|
"pacr",
|
||||||
"power",
|
{"device_class": "power"},
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
SENSOR_TYPES = {**TOTAL_SENSOR_TYPES, **INVERTER_SENSOR_TYPES}
|
STORAGE_SENSOR_TYPES = {
|
||||||
|
"storage_storage_production_today": (
|
||||||
|
"Storage production today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eBatDisChargeToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_storage_production_lifetime": (
|
||||||
|
"Lifetime Storage production",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eBatDisChargeTotal",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_grid_discharge_today": (
|
||||||
|
"Grid discharged today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eacDisChargeToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_load_consumption_today": (
|
||||||
|
"Load consumption today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eopDischrToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_load_consumption_lifetime": (
|
||||||
|
"Lifetime load consumption",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eopDischrTotal",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_grid_charged_today": (
|
||||||
|
"Grid charged today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eacChargeToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_charge_storage_lifetime": (
|
||||||
|
"Lifetime storaged charged",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eChargeTotal",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_solar_production": (
|
||||||
|
"Solar power production",
|
||||||
|
POWER_WATT,
|
||||||
|
"ppv",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"storage_battery_percentage": (
|
||||||
|
"Battery percentage",
|
||||||
|
"%",
|
||||||
|
"capacity",
|
||||||
|
{"device_class": "battery"},
|
||||||
|
),
|
||||||
|
"storage_power_flow": (
|
||||||
|
"Storage charging/ discharging(-ve)",
|
||||||
|
POWER_WATT,
|
||||||
|
"pCharge",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"storage_load_consumption_solar_storage": (
|
||||||
|
"Load consumption(Solar + Storage)",
|
||||||
|
"VA",
|
||||||
|
"rateVA",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_charge_today": (
|
||||||
|
"Charge today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eChargeToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_import_from_grid": (
|
||||||
|
"Import from grid",
|
||||||
|
POWER_WATT,
|
||||||
|
"pAcInPut",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"storage_import_from_grid_today": (
|
||||||
|
"Import from grid today",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eToUserToday",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_import_from_grid_total": (
|
||||||
|
"Import from grid total",
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
"eToUserTotal",
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
"storage_load_consumption": (
|
||||||
|
"Load consumption",
|
||||||
|
POWER_WATT,
|
||||||
|
"outPutPower",
|
||||||
|
{"device_class": "power"},
|
||||||
|
),
|
||||||
|
"storage_grid_voltage": ("AC input voltage", VOLT, "vGrid", {"round": 2}),
|
||||||
|
"storage_pv_charging_voltage": ("PV charging voltage", VOLT, "vpv", {"round": 2}),
|
||||||
|
"storage_ac_input_frequency_out": (
|
||||||
|
"AC input frequency",
|
||||||
|
FREQUENCY_HERTZ,
|
||||||
|
"freqOutPut",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_output_voltage": ("Output voltage", VOLT, "outPutVolt", {"round": 2}),
|
||||||
|
"storage_ac_output_frequency": (
|
||||||
|
"Ac output frequency",
|
||||||
|
FREQUENCY_HERTZ,
|
||||||
|
"freqGrid",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_current_PV": (
|
||||||
|
"Solar charge current",
|
||||||
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
|
"iAcCharge",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_current_1": (
|
||||||
|
"Solar current to storage",
|
||||||
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
|
"iChargePV1",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_grid_amperage_input": (
|
||||||
|
"Grid charge current",
|
||||||
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
|
"chgCurr",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_grid_out_current": (
|
||||||
|
"Grid out current",
|
||||||
|
ELECTRICAL_CURRENT_AMPERE,
|
||||||
|
"outPutCurrent",
|
||||||
|
{"round": 2},
|
||||||
|
),
|
||||||
|
"storage_battery_voltage": ("Battery voltage", VOLT, "vBat", {"round": 2}),
|
||||||
|
"storage_load_percentage": (
|
||||||
|
"Load percentage",
|
||||||
|
"%",
|
||||||
|
"loadPercent",
|
||||||
|
{"device_class": "battery", "round": 2},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
SENSOR_TYPES = {**TOTAL_SENSOR_TYPES, **INVERTER_SENSOR_TYPES, **STORAGE_SENSOR_TYPES}
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
|
@ -134,25 +306,34 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||||
plant_info = api.plant_list(user_id)
|
plant_info = api.plant_list(user_id)
|
||||||
plant_id = plant_info["data"][0]["plantId"]
|
plant_id = plant_info["data"][0]["plantId"]
|
||||||
|
|
||||||
# Get a list of inverters for specified plant to add sensors for.
|
# Get a list of devices for specified plant to add sensors for.
|
||||||
inverters = api.inverter_list(plant_id)
|
devices = api.device_list(plant_id)
|
||||||
entities = []
|
entities = []
|
||||||
probe = GrowattData(api, username, password, plant_id, True)
|
probe = GrowattData(api, username, password, plant_id, "total")
|
||||||
for sensor in TOTAL_SENSOR_TYPES:
|
for sensor in TOTAL_SENSOR_TYPES:
|
||||||
entities.append(
|
entities.append(
|
||||||
GrowattInverter(probe, f"{name} Total", sensor, f"{plant_id}-{sensor}")
|
GrowattInverter(probe, f"{name} Total", sensor, f"{plant_id}-{sensor}")
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add sensors for each inverter in the specified plant.
|
# Add sensors for each device in the specified plant.
|
||||||
for inverter in inverters:
|
for device in devices:
|
||||||
probe = GrowattData(api, username, password, inverter["deviceSn"], False)
|
probe = GrowattData(
|
||||||
for sensor in INVERTER_SENSOR_TYPES:
|
api, username, password, device["deviceSn"], device["deviceType"]
|
||||||
|
)
|
||||||
|
sensors = []
|
||||||
|
if device["deviceType"] == "inverter":
|
||||||
|
sensors = INVERTER_SENSOR_TYPES
|
||||||
|
elif device["deviceType"] == "storage":
|
||||||
|
probe.plant_id = plant_id
|
||||||
|
sensors = STORAGE_SENSOR_TYPES
|
||||||
|
|
||||||
|
for sensor in sensors:
|
||||||
entities.append(
|
entities.append(
|
||||||
GrowattInverter(
|
GrowattInverter(
|
||||||
probe,
|
probe,
|
||||||
f"{inverter['deviceAilas']}",
|
f"{device['deviceAilas']}",
|
||||||
sensor,
|
sensor,
|
||||||
f"{inverter['deviceSn']}-{sensor}",
|
f"{device['deviceSn']}-{sensor}",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -188,12 +369,16 @@ class GrowattInverter(Entity):
|
||||||
@property
|
@property
|
||||||
def state(self):
|
def state(self):
|
||||||
"""Return the state of the sensor."""
|
"""Return the state of the sensor."""
|
||||||
return self.probe.get_data(SENSOR_TYPES[self.sensor][2])
|
result = self.probe.get_data(SENSOR_TYPES[self.sensor][2])
|
||||||
|
round_to = SENSOR_TYPES[self.sensor][3].get("round")
|
||||||
|
if round_to is not None:
|
||||||
|
result = round(result, round_to)
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
"""Return the device class of the sensor."""
|
"""Return the device class of the sensor."""
|
||||||
return SENSOR_TYPES[self.sensor][3]
|
return SENSOR_TYPES[self.sensor][3].get("device_class")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_of_measurement(self):
|
def unit_of_measurement(self):
|
||||||
|
@ -208,12 +393,13 @@ class GrowattInverter(Entity):
|
||||||
class GrowattData:
|
class GrowattData:
|
||||||
"""The class for handling data retrieval."""
|
"""The class for handling data retrieval."""
|
||||||
|
|
||||||
def __init__(self, api, username, password, inverter_id, is_total=False):
|
def __init__(self, api, username, password, device_id, growatt_type):
|
||||||
"""Initialize the probe."""
|
"""Initialize the probe."""
|
||||||
|
|
||||||
self.is_total = is_total
|
self.growatt_type = growatt_type
|
||||||
self.api = api
|
self.api = api
|
||||||
self.inverter_id = inverter_id
|
self.device_id = device_id
|
||||||
|
self.plant_id = None
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
|
@ -222,19 +408,27 @@ class GrowattData:
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update probe data."""
|
"""Update probe data."""
|
||||||
self.api.login(self.username, self.password)
|
self.api.login(self.username, self.password)
|
||||||
_LOGGER.debug("Updating data for %s", self.inverter_id)
|
_LOGGER.debug("Updating data for %s", self.device_id)
|
||||||
try:
|
try:
|
||||||
if self.is_total:
|
if self.growatt_type == "total":
|
||||||
total_info = self.api.plant_info(self.inverter_id)
|
total_info = self.api.plant_info(self.device_id)
|
||||||
del total_info["deviceList"]
|
del total_info["deviceList"]
|
||||||
# PlantMoneyText comes in as "3.1/€" remove anything that isn't part of the number
|
# PlantMoneyText comes in as "3.1/€" remove anything that isn't part of the number
|
||||||
total_info["plantMoneyText"] = re.sub(
|
total_info["plantMoneyText"] = re.sub(
|
||||||
r"[^\d.,]", "", total_info["plantMoneyText"]
|
r"[^\d.,]", "", total_info["plantMoneyText"]
|
||||||
)
|
)
|
||||||
self.data = total_info
|
self.data = total_info
|
||||||
else:
|
elif self.growatt_type == "inverter":
|
||||||
inverter_info = self.api.inverter_detail(self.inverter_id)
|
inverter_info = self.api.inverter_detail(self.device_id)
|
||||||
self.data = inverter_info["data"]
|
self.data = inverter_info["data"]
|
||||||
|
elif self.growatt_type == "storage":
|
||||||
|
storage_info_detail = self.api.storage_params(self.device_id)[
|
||||||
|
"storageDetailBean"
|
||||||
|
]
|
||||||
|
storage_energy_overview = self.api.storage_energy_overview(
|
||||||
|
self.plant_id, self.device_id
|
||||||
|
)
|
||||||
|
self.data = {**storage_info_detail, **storage_energy_overview}
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
_LOGGER.error("Unable to fetch data from Growatt server")
|
_LOGGER.error("Unable to fetch data from Growatt server")
|
||||||
|
|
||||||
|
|
|
@ -687,7 +687,7 @@ greenwavereality==0.5.1
|
||||||
griddypower==0.1.0
|
griddypower==0.1.0
|
||||||
|
|
||||||
# homeassistant.components.growatt_server
|
# homeassistant.components.growatt_server
|
||||||
growattServer==0.0.1
|
growattServer==0.0.4
|
||||||
|
|
||||||
# homeassistant.components.gstreamer
|
# homeassistant.components.gstreamer
|
||||||
gstreamer-player==1.1.2
|
gstreamer-player==1.1.2
|
||||||
|
|
Loading…
Reference in New Issue