diff --git a/homeassistant/components/solax/manifest.json b/homeassistant/components/solax/manifest.json new file mode 100644 index 00000000000..57b31a5e4b1 --- /dev/null +++ b/homeassistant/components/solax/manifest.json @@ -0,0 +1,11 @@ +{ + "domain": "solax", + "name": "Solax Inverter", + "documentation": "https://www.home-assistant.io/components/solax", + "requirements": [ + "solax==0.0.3" + ], + "dependencies": [], + "codeowners": [] + } + \ No newline at end of file diff --git a/homeassistant/components/solax/sensor.py b/homeassistant/components/solax/sensor.py index f125b06dc55..88ae4992727 100644 --- a/homeassistant/components/solax/sensor.py +++ b/homeassistant/components/solax/sensor.py @@ -5,8 +5,6 @@ import json from datetime import timedelta import logging -import aiohttp -import async_timeout import voluptuous as vol from homeassistant.const import ( @@ -22,141 +20,37 @@ from homeassistant.helpers.event import async_track_time_interval _LOGGER = logging.getLogger(__name__) -# key: name of sensor -# value.0: index -# value.1: unit (String) or None -# from https://github.com/GitHobi/solax/wiki/direct-data-retrieval -INVERTER_SENSORS = { - 'PV1 Current': (0, 'A'), - 'PV2 Current': (1, 'A'), - 'PV1 Voltage': (2, 'V'), - 'PV2 Voltage': (3, 'V'), - - 'Output Current': (4, 'A'), - 'Network Voltage': (5, 'V'), - 'Power Now': (6, 'W'), - - 'Inverter Temperature': (7, TEMP_CELSIUS), - 'Today\'s Energy': (8, 'kWh'), - 'Total Energy': (9, 'kWh'), - 'Exported Power': (10, 'W'), - 'PV1 Power': (11, 'W'), - 'PV2 Power': (12, 'W'), - - 'Battery Voltage': (13, 'V'), - 'Battery Current': (14, 'A'), - 'Battery Power': (15, 'W'), - 'Battery Temperature': (16, TEMP_CELSIUS), - 'Battery Remaining Capacity': (17, '%'), - - 'Battery Energy': (19, 'kWh'), - - 'Grid Frequency': (50, 'Hz'), - 'EPS Voltage': (53, 'V'), - 'EPS Current': (54, 'A'), - 'EPS Power': (55, 'W'), - 'EPS Frequency': (56, 'Hz'), -} - PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_IP_ADDRESS): cv.string, }) SCAN_INTERVAL = timedelta(seconds=30) -REQUEST_TIMEOUT = 5 - -REAL_TIME_DATA_ENDPOINT = 'http://{ip_address}/api/realTimeData.htm' - -DATA_SCHEMA = vol.Schema( - vol.All([vol.Coerce(float)], vol.Length(min=68, max=68)) -) - -REAL_TIME_DATA_SCHEMA = vol.Schema({ - vol.Required('method'): cv.string, - vol.Required('version'): cv.string, - vol.Required('type'): cv.string, - vol.Required('SN'): cv.string, - vol.Required('Data'): DATA_SCHEMA, - vol.Required('Status'): cv.positive_int, -}, extra=vol.REMOVE_EXTRA) - - -class SolaxRequestError(Exception): - """Error to indicate a Solax API request has failed.""" - - pass - async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Platform setup.""" - endpoint = RealTimeDataEndpoint(hass, config.get(CONF_IP_ADDRESS)) + import solax + + api = solax.solax.RealTimeAPI(config.get(CONF_IP_ADDRESS)) + endpoint = RealTimeDataEndpoint(hass, api) hass.async_add_job(endpoint.async_refresh) async_track_time_interval(hass, endpoint.async_refresh, SCAN_INTERVAL) devices = [] - for sensor in INVERTER_SENSORS: - devices.append(Inverter(sensor)) + for sensor in solax.INVERTER_SENSORS: + unit = solax.INVERTER_SENSORS[sensor][1] + if unit == 'C': + unit = TEMP_CELSIUS + devices.append(Inverter(sensor, unit)) endpoint.sensors = devices async_add_entities(devices) - -async def async_solax_real_time_request(hass, schema, ip_address, retry, - t_wait=0): - """Make call to inverter endpoint.""" - if t_wait > 0: - msg = "Timeout connecting to Solax inverter, waiting %d to retry." - _LOGGER.error(msg, t_wait) - asyncio.sleep(t_wait) - new_wait = (t_wait*2)+5 - retry = retry - 1 - try: - session = async_get_clientsession(hass) - - with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop): - url = REAL_TIME_DATA_ENDPOINT.format(ip_address=ip_address) - req = await session.get(url) - garbage = await req.read() - formatted = garbage.decode("utf-8") - formatted = formatted.replace(",,", ",0.0,").replace(",,", ",0.0,") - json_response = json.loads(formatted) - return schema(json_response) - except asyncio.TimeoutError: - if retry > 0: - return await async_solax_real_time_request(hass, - schema, - ip_address, - retry, - new_wait) - _LOGGER.error("Too many timeouts connecting to Solax.") - except (aiohttp.ClientError) as client_err: - _LOGGER.error("Could not connect to Solax API endpoint") - _LOGGER.error(client_err) - except ValueError: - _LOGGER.error("Received non-JSON data from Solax API endpoint") - except vol.Invalid as err: - _LOGGER.error("Received unexpected JSON from Solax" - " API endpoint: %s", err) - _LOGGER.error(json_response) - raise SolaxRequestError - - -def parse_solax_battery_response(response): - """Manipulate the response from solax endpoint.""" - data_list = response['Data'] - result = {} - for name, index in INVERTER_SENSORS.items(): - response_index = index[0] - result[name] = data_list[response_index] - return result - - class RealTimeDataEndpoint: """Representation of a Sensor.""" - def __init__(self, hass, ip_address): + def __init__(self, hass, api): """Initialize the sensor.""" self.hass = hass - self.ip_address = ip_address + self.api = api self.data = {} self.ready = asyncio.Event() self.sensors = [] @@ -167,11 +61,7 @@ class RealTimeDataEndpoint: This is the only method that should fetch new data for Home Assistant. """ try: - resp = await async_solax_real_time_request(self.hass, - REAL_TIME_DATA_SCHEMA, - self.ip_address, - 3) - self.data = parse_solax_battery_response(resp) + self.data = await self.api.get_data() self.ready.set() except SolaxRequestError: if now is not None: @@ -187,10 +77,11 @@ class RealTimeDataEndpoint: class Inverter(Entity): """Class for a sensor.""" - def __init__(self, key): + def __init__(self, key, unit): """Initialize an inverter sensor.""" self.key = key self.value = None + self.unit = unit @property def state(self): @@ -205,7 +96,7 @@ class Inverter(Entity): @property def unit_of_measurement(self): """Return the unit of measurement.""" - return INVERTER_SENSORS[self.key][1] + return self.unit @property def should_poll(self): diff --git a/requirements_all.txt b/requirements_all.txt index b64079b4460..d98111b8d27 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1595,6 +1595,9 @@ socialbladeclient==0.2 # homeassistant.components.solaredge solaredge==0.0.2 +# homeassistant.components.solax +solax==0.0.3 + # homeassistant.components.honeywell somecomfort==0.5.2 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 8f6172c2323..d2e5083acf0 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -124,6 +124,7 @@ TEST_REQUIREMENTS = ( 'simplisafe-python', 'sleepyq', 'smhi-pkg', + 'solax' 'somecomfort', 'sqlalchemy', 'srpenergy',