mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Add growatt server integration (#25635)
* Added growatt server integration * Ran black formatter * Processed feedback. * Made attributes more readable. * Create a sensor for each property. * Added unique_ids * Accidentally flipped functions * Added dynamic device classes. * Fixed stale session.
This commit is contained in:
parent
051639b6ad
commit
a4fd991ab5
@ -249,6 +249,7 @@ omit =
|
||||
homeassistant/components/greeneye_monitor/sensor.py
|
||||
homeassistant/components/greenwave/light.py
|
||||
homeassistant/components/group/notify.py
|
||||
homeassistant/components/growatt_server/sensor.py
|
||||
homeassistant/components/gstreamer/media_player.py
|
||||
homeassistant/components/gtfs/sensor.py
|
||||
homeassistant/components/gtt/sensor.py
|
||||
|
@ -110,6 +110,7 @@ homeassistant/components/google_translate/* @awarecan
|
||||
homeassistant/components/google_travel_time/* @robbiet480
|
||||
homeassistant/components/gpsd/* @fabaff
|
||||
homeassistant/components/group/* @home-assistant/core
|
||||
homeassistant/components/growatt_server/* @indykoning
|
||||
homeassistant/components/gtfs/* @robbiet480
|
||||
homeassistant/components/harmony/* @ehendrix23
|
||||
homeassistant/components/hassio/* @home-assistant/hass-io
|
||||
|
1
homeassistant/components/growatt_server/__init__.py
Normal file
1
homeassistant/components/growatt_server/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""The Growatt server PV inverter sensor integration."""
|
12
homeassistant/components/growatt_server/manifest.json
Normal file
12
homeassistant/components/growatt_server/manifest.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"domain": "growatt_server",
|
||||
"name": "Growatt Server",
|
||||
"documentation": "https://www.home-assistant.io/components/growatt_server/",
|
||||
"requirements": [
|
||||
"growattServer==0.0.1"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": [
|
||||
"@indykoning"
|
||||
]
|
||||
}
|
189
homeassistant/components/growatt_server/sensor.py
Normal file
189
homeassistant/components/growatt_server/sensor.py
Normal file
@ -0,0 +1,189 @@
|
||||
"""Read status of growatt inverters."""
|
||||
import re
|
||||
import json
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
import growattServer
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.util import Throttle
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME, CONF_USERNAME, CONF_PASSWORD
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONF_PLANT_ID = "plant_id"
|
||||
DEFAULT_PLANT_ID = "0"
|
||||
DEFAULT_NAME = "Growatt"
|
||||
SCAN_INTERVAL = datetime.timedelta(minutes=5)
|
||||
|
||||
TOTAL_SENSOR_TYPES = {
|
||||
"total_money_today": ("Total money today", "€", "plantMoneyText", None),
|
||||
"total_money_total": ("Money lifetime", "€", "totalMoneyText", None),
|
||||
"total_energy_today": ("Energy Today", "kWh", "todayEnergy", "power"),
|
||||
"total_output_power": ("Output Power", "W", "invTodayPpv", "power"),
|
||||
"total_energy_output": ("Lifetime energy output", "kWh", "totalEnergy", "power"),
|
||||
"total_maximum_output": ("Maximum power", "W", "nominalPower", "power"),
|
||||
}
|
||||
|
||||
INVERTER_SENSOR_TYPES = {
|
||||
"inverter_energy_today": ("Energy today", "kWh", "e_today", "power"),
|
||||
"inverter_energy_total": ("Lifetime energy output", "kWh", "e_total", "power"),
|
||||
"inverter_voltage_input_1": ("Input 1 voltage", "V", "vpv1", None),
|
||||
"inverter_amperage_input_1": ("Input 1 Amperage", "A", "ipv1", None),
|
||||
"inverter_wattage_input_1": ("Input 1 Wattage", "W", "ppv1", "power"),
|
||||
"inverter_voltage_input_2": ("Input 2 voltage", "V", "vpv2", None),
|
||||
"inverter_amperage_input_2": ("Input 2 Amperage", "A", "ipv2", None),
|
||||
"inverter_wattage_input_2": ("Input 2 Wattage", "W", "ppv2", "power"),
|
||||
"inverter_voltage_input_3": ("Input 3 voltage", "V", "vpv3", None),
|
||||
"inverter_amperage_input_3": ("Input 3 Amperage", "A", "ipv3", None),
|
||||
"inverter_wattage_input_3": ("Input 3 Wattage", "W", "ppv3", "power"),
|
||||
"inverter_internal_wattage": ("Internal wattage", "W", "ppv", "power"),
|
||||
"inverter_reactive_voltage": ("Reactive voltage", "V", "vacr", None),
|
||||
"inverter_inverter_reactive_amperage": ("Reactive amperage", "A", "iacr", None),
|
||||
"inverter_frequency": ("AC frequency", "Hz", "fac", None),
|
||||
"inverter_current_wattage": ("Output power", "W", "pac", "power"),
|
||||
"inverter_current_reactive_wattage": ("Reactive wattage", "W", "pacr", "power"),
|
||||
}
|
||||
|
||||
SENSOR_TYPES = {**TOTAL_SENSOR_TYPES, **INVERTER_SENSOR_TYPES}
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_PLANT_ID, default=DEFAULT_PLANT_ID): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Growatt sensor."""
|
||||
username = config[CONF_USERNAME]
|
||||
password = config[CONF_PASSWORD]
|
||||
plant_id = config[CONF_PLANT_ID]
|
||||
name = config[CONF_NAME]
|
||||
|
||||
api = growattServer.GrowattApi()
|
||||
|
||||
# Log in to api and fetch first plant if no plant id is defined.
|
||||
login_response = api.login(username, password)
|
||||
if not login_response["success"] and login_response["errCode"] == "102":
|
||||
_LOGGER.error("Username or Password may be incorrect!")
|
||||
return
|
||||
user_id = login_response["userId"]
|
||||
if plant_id == DEFAULT_PLANT_ID:
|
||||
plant_info = api.plant_list(user_id)
|
||||
plant_id = plant_info["data"][0]["plantId"]
|
||||
|
||||
# Get a list of inverters for specified plant to add sensors for.
|
||||
inverters = api.inverter_list(plant_id)
|
||||
entities = []
|
||||
probe = GrowattData(api, username, password, plant_id, True)
|
||||
for sensor in TOTAL_SENSOR_TYPES:
|
||||
entities.append(
|
||||
GrowattInverter(probe, f"{name} Total", sensor, f"{plant_id}-{sensor}")
|
||||
)
|
||||
|
||||
# Add sensors for each inverter in the specified plant.
|
||||
for inverter in inverters:
|
||||
probe = GrowattData(api, username, password, inverter["deviceSn"], False)
|
||||
for sensor in INVERTER_SENSOR_TYPES:
|
||||
entities.append(
|
||||
GrowattInverter(
|
||||
probe,
|
||||
f"{inverter['deviceAilas']}",
|
||||
sensor,
|
||||
f"{inverter['deviceSn']}-{sensor}",
|
||||
)
|
||||
)
|
||||
|
||||
add_entities(entities, True)
|
||||
|
||||
|
||||
class GrowattInverter(Entity):
|
||||
"""Representation of a Growatt Sensor."""
|
||||
|
||||
def __init__(self, probe, name, sensor, unique_id):
|
||||
"""Initialize a PVOutput sensor."""
|
||||
self.sensor = sensor
|
||||
self.probe = probe
|
||||
self._name = name
|
||||
self._state = None
|
||||
self._unique_id = unique_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return f"{self._name} {SENSOR_TYPES[self.sensor][0]}"
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the unique id of the sensor."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of the sensor."""
|
||||
return "mdi:solar-power"
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self.probe.get_data(SENSOR_TYPES[self.sensor][2])
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class of the sensor."""
|
||||
return SENSOR_TYPES[self.sensor][3]
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity, if any."""
|
||||
return SENSOR_TYPES[self.sensor][1]
|
||||
|
||||
def update(self):
|
||||
"""Get the latest data from the Growat API and updates the state."""
|
||||
self.probe.update()
|
||||
|
||||
|
||||
class GrowattData:
|
||||
"""The class for handling data retrieval."""
|
||||
|
||||
def __init__(self, api, username, password, inverter_id, is_total=False):
|
||||
"""Initialize the probe."""
|
||||
|
||||
self.is_total = is_total
|
||||
self.api = api
|
||||
self.inverter_id = inverter_id
|
||||
self.data = {}
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
@Throttle(SCAN_INTERVAL)
|
||||
def update(self):
|
||||
"""Update probe data."""
|
||||
self.api.login(self.username, self.password)
|
||||
_LOGGER.debug("Updating data for %s", self.inverter_id)
|
||||
try:
|
||||
if self.is_total:
|
||||
total_info = self.api.plant_info(self.inverter_id)
|
||||
del total_info["deviceList"]
|
||||
# PlantMoneyText comes in as "3.1/€" remove anything that isn't part of the number
|
||||
total_info["plantMoneyText"] = re.sub(
|
||||
r"[^\d.,]", "", total_info["plantMoneyText"]
|
||||
)
|
||||
self.data = total_info
|
||||
else:
|
||||
inverter_info = self.api.inverter_detail(self.inverter_id)
|
||||
self.data = inverter_info["data"]
|
||||
except json.decoder.JSONDecodeError:
|
||||
_LOGGER.error("Unable to fetch data from Growatt server")
|
||||
|
||||
def get_data(self, variable):
|
||||
"""Get the data."""
|
||||
return self.data.get(variable)
|
@ -585,6 +585,9 @@ greeneye_monitor==1.0
|
||||
# homeassistant.components.greenwave
|
||||
greenwavereality==0.5.1
|
||||
|
||||
# homeassistant.components.growatt_server
|
||||
growattServer==0.0.1
|
||||
|
||||
# homeassistant.components.gstreamer
|
||||
gstreamer-player==1.1.2
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user