diff --git a/homeassistant/components/powerwall/__init__.py b/homeassistant/components/powerwall/__init__.py index a5401206379..d5c7a534180 100644 --- a/homeassistant/components/powerwall/__init__.py +++ b/homeassistant/components/powerwall/__init__.py @@ -3,6 +3,7 @@ import asyncio from datetime import timedelta import logging +import requests from tesla_powerwall import ( ApiError, MetersResponse, @@ -21,12 +22,15 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import ( DOMAIN, POWERWALL_API_CHARGE, + POWERWALL_API_DEVICE_TYPE, POWERWALL_API_GRID_STATUS, POWERWALL_API_METERS, + POWERWALL_API_SITE_INFO, POWERWALL_API_SITEMASTER, + POWERWALL_API_STATUS, POWERWALL_COORDINATOR, + POWERWALL_HTTP_SESSION, POWERWALL_OBJECT, - POWERWALL_SITE_INFO, UPDATE_INTERVAL, ) @@ -62,10 +66,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): entry_id = entry.entry_id hass.data[DOMAIN].setdefault(entry_id, {}) - power_wall = PowerWall(entry.data[CONF_IP_ADDRESS]) + http_session = requests.Session() + power_wall = PowerWall(entry.data[CONF_IP_ADDRESS], http_session=http_session) try: - site_info = await hass.async_add_executor_job(call_site_info, power_wall) + powerwall_data = await hass.async_add_executor_job(call_base_info, power_wall) except (PowerWallUnreachableError, ApiError, ConnectionError): + http_session.close() raise ConfigEntryNotReady async def async_update_data(): @@ -80,11 +86,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): update_interval=timedelta(seconds=UPDATE_INTERVAL), ) - hass.data[DOMAIN][entry.entry_id] = { - POWERWALL_OBJECT: power_wall, - POWERWALL_COORDINATOR: coordinator, - POWERWALL_SITE_INFO: site_info, - } + hass.data[DOMAIN][entry.entry_id] = powerwall_data + hass.data[DOMAIN][entry.entry_id].update( + { + POWERWALL_OBJECT: power_wall, + POWERWALL_COORDINATOR: coordinator, + POWERWALL_HTTP_SESSION: http_session, + } + ) await coordinator.async_refresh() @@ -96,9 +105,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): return True -def call_site_info(power_wall): - """Wrap site_info to be a callable.""" - return power_wall.site_info +def call_base_info(power_wall): + """Wrap powerwall properties to be a callable.""" + return { + POWERWALL_API_SITE_INFO: power_wall.site_info, + POWERWALL_API_STATUS: power_wall.status, + POWERWALL_API_DEVICE_TYPE: power_wall.device_type, + } def _fetch_powerwall_data(power_wall): @@ -124,6 +137,9 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ] ) ) + + hass.data[DOMAIN][entry.entry_id][POWERWALL_HTTP_SESSION].close() + if unload_ok: hass.data[DOMAIN].pop(entry.entry_id) diff --git a/homeassistant/components/powerwall/binary_sensor.py b/homeassistant/components/powerwall/binary_sensor.py index 52b82531472..329b26221b8 100644 --- a/homeassistant/components/powerwall/binary_sensor.py +++ b/homeassistant/components/powerwall/binary_sensor.py @@ -12,13 +12,15 @@ from .const import ( ATTR_NOMINAL_SYSTEM_POWER, ATTR_REGION, DOMAIN, + POWERWALL_API_DEVICE_TYPE, POWERWALL_API_GRID_STATUS, + POWERWALL_API_SITE_INFO, POWERWALL_API_SITEMASTER, + POWERWALL_API_STATUS, POWERWALL_CONNECTED_KEY, POWERWALL_COORDINATOR, POWERWALL_GRID_ONLINE, POWERWALL_RUNNING_KEY, - POWERWALL_SITE_INFO, SITE_INFO_GRID_CODE, SITE_INFO_NOMINAL_SYSTEM_POWER_KW, SITE_INFO_REGION, @@ -33,7 +35,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities): powerwall_data = hass.data[DOMAIN][config_entry.entry_id] coordinator = powerwall_data[POWERWALL_COORDINATOR] - site_info = powerwall_data[POWERWALL_SITE_INFO] + site_info = powerwall_data[POWERWALL_API_SITE_INFO] + device_type = powerwall_data[POWERWALL_API_DEVICE_TYPE] + status = powerwall_data[POWERWALL_API_STATUS] entities = [] for sensor_class in ( @@ -41,7 +45,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): PowerWallGridStatusSensor, PowerWallConnectedSensor, ): - entities.append(sensor_class(coordinator, site_info)) + entities.append(sensor_class(coordinator, site_info, status, device_type)) async_add_entities(entities, True) diff --git a/homeassistant/components/powerwall/config_flow.py b/homeassistant/components/powerwall/config_flow.py index e94b0cd4056..7e1b3eb3fb1 100644 --- a/homeassistant/components/powerwall/config_flow.py +++ b/homeassistant/components/powerwall/config_flow.py @@ -7,7 +7,6 @@ import voluptuous as vol from homeassistant import config_entries, core, exceptions from homeassistant.const import CONF_IP_ADDRESS -from . import call_site_info from .const import DOMAIN # pylint:disable=unused-import from .const import POWERWALL_SITE_NAME @@ -33,6 +32,11 @@ async def validate_input(hass: core.HomeAssistant, data): return {"title": site_info[POWERWALL_SITE_NAME]} +def call_site_info(power_wall): + """Wrap site_info to be a callable.""" + return power_wall.site_info + + class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a config flow for Tesla Powerwall.""" diff --git a/homeassistant/components/powerwall/const.py b/homeassistant/components/powerwall/const.py index 59accc9e9a3..2e9c3739c48 100644 --- a/homeassistant/components/powerwall/const.py +++ b/homeassistant/components/powerwall/const.py @@ -6,7 +6,6 @@ POWERWALL_SITE_NAME = "site_name" POWERWALL_OBJECT = "powerwall" POWERWALL_COORDINATOR = "coordinator" -POWERWALL_SITE_INFO = "site_info" UPDATE_INTERVAL = 60 @@ -24,12 +23,21 @@ SITE_INFO_NOMINAL_SYSTEM_POWER_KW = "nominal_system_power_kW" SITE_INFO_NOMINAL_SYSTEM_ENERGY_KWH = "nominal_system_energy_kWh" SITE_INFO_REGION = "region" +DEVICE_TYPE_DEVICE_TYPE = "device_type" + +STATUS_VERSION = "version" + POWERWALL_SITE_NAME = "site_name" POWERWALL_API_METERS = "meters" POWERWALL_API_CHARGE = "charge" POWERWALL_API_GRID_STATUS = "grid_status" POWERWALL_API_SITEMASTER = "sitemaster" +POWERWALL_API_STATUS = "status" +POWERWALL_API_DEVICE_TYPE = "device_type" +POWERWALL_API_SITE_INFO = "site_info" + +POWERWALL_HTTP_SESSION = "http_session" POWERWALL_GRID_ONLINE = "SystemGridConnected" POWERWALL_CONNECTED_KEY = "connected_to_tesla" diff --git a/homeassistant/components/powerwall/entity.py b/homeassistant/components/powerwall/entity.py index 04bb75fd47a..c09a1aca612 100644 --- a/homeassistant/components/powerwall/entity.py +++ b/homeassistant/components/powerwall/entity.py @@ -3,6 +3,7 @@ from homeassistant.helpers.entity import Entity from .const import ( + DEVICE_TYPE_DEVICE_TYPE, DOMAIN, MANUFACTURER, MODEL, @@ -10,17 +11,20 @@ from .const import ( SITE_INFO_GRID_CODE, SITE_INFO_NOMINAL_SYSTEM_ENERGY_KWH, SITE_INFO_UTILITY, + STATUS_VERSION, ) class PowerWallEntity(Entity): """Base class for powerwall entities.""" - def __init__(self, coordinator, site_info): + def __init__(self, coordinator, site_info, status, device_type): """Initialize the sensor.""" super().__init__() self._coordinator = coordinator self._site_info = site_info + self._device_type = device_type.get(DEVICE_TYPE_DEVICE_TYPE) + self._version = status.get(STATUS_VERSION) # This group of properties will be unique to to the site unique_group = ( site_info[SITE_INFO_UTILITY], @@ -32,12 +36,18 @@ class PowerWallEntity(Entity): @property def device_info(self): """Powerwall device info.""" - return { + device_info = { "identifiers": {(DOMAIN, self.base_unique_id)}, "name": self._site_info[POWERWALL_SITE_NAME], "manufacturer": MANUFACTURER, - "model": MODEL, } + model = MODEL + if self._device_type: + model += f" ({self._device_type})" + device_info["model"] = model + if self._version: + device_info["sw_version"] = self._version + return device_info @property def available(self): diff --git a/homeassistant/components/powerwall/manifest.json b/homeassistant/components/powerwall/manifest.json index ed90bc339fc..951ad960e14 100644 --- a/homeassistant/components/powerwall/manifest.json +++ b/homeassistant/components/powerwall/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/powerwall", "requirements": [ - "tesla-powerwall==0.1.1" + "tesla-powerwall==0.1.3" ], "ssdp": [], "zeroconf": [], @@ -13,4 +13,4 @@ "codeowners": [ "@bdraco" ] -} \ No newline at end of file +} diff --git a/homeassistant/components/powerwall/sensor.py b/homeassistant/components/powerwall/sensor.py index 3ebb467d4fc..cf49b36a570 100644 --- a/homeassistant/components/powerwall/sensor.py +++ b/homeassistant/components/powerwall/sensor.py @@ -15,9 +15,11 @@ from .const import ( ATTR_INSTANT_AVERAGE_VOLTAGE, DOMAIN, POWERWALL_API_CHARGE, + POWERWALL_API_DEVICE_TYPE, POWERWALL_API_METERS, + POWERWALL_API_SITE_INFO, + POWERWALL_API_STATUS, POWERWALL_COORDINATOR, - POWERWALL_SITE_INFO, ) from .entity import PowerWallEntity @@ -30,13 +32,17 @@ async def async_setup_entry(hass, config_entry, async_add_entities): _LOGGER.debug("Powerwall_data: %s", powerwall_data) coordinator = powerwall_data[POWERWALL_COORDINATOR] - site_info = powerwall_data[POWERWALL_SITE_INFO] + site_info = powerwall_data[POWERWALL_API_SITE_INFO] + device_type = powerwall_data[POWERWALL_API_DEVICE_TYPE] + status = powerwall_data[POWERWALL_API_STATUS] entities = [] for meter in coordinator.data[POWERWALL_API_METERS]: - entities.append(PowerWallEnergySensor(meter, coordinator, site_info)) + entities.append( + PowerWallEnergySensor(meter, coordinator, site_info, status, device_type) + ) - entities.append(PowerWallChargeSensor(coordinator, site_info)) + entities.append(PowerWallChargeSensor(coordinator, site_info, status, device_type)) async_add_entities(entities, True) @@ -73,9 +79,9 @@ class PowerWallChargeSensor(PowerWallEntity): class PowerWallEnergySensor(PowerWallEntity): """Representation of an Powerwall Energy sensor.""" - def __init__(self, meter, coordinator, site_info): + def __init__(self, meter, coordinator, site_info, status, device_type): """Initialize the sensor.""" - super().__init__(coordinator, site_info) + super().__init__(coordinator, site_info, status, device_type) self._meter = meter @property diff --git a/requirements_all.txt b/requirements_all.txt index 46fd27fa1fd..eefa3ce8a02 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2011,7 +2011,7 @@ temperusb==1.5.3 # tensorflow==1.13.2 # homeassistant.components.powerwall -tesla-powerwall==0.1.1 +tesla-powerwall==0.1.3 # homeassistant.components.tesla teslajsonpy==0.6.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 09542dddd54..5dc69a651e6 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -729,7 +729,7 @@ sunwatcher==0.2.1 tellduslive==0.10.10 # homeassistant.components.powerwall -tesla-powerwall==0.1.1 +tesla-powerwall==0.1.3 # homeassistant.components.tesla teslajsonpy==0.6.0 diff --git a/tests/components/powerwall/mocks.py b/tests/components/powerwall/mocks.py index 330f1123b8f..aba6ecfeb23 100644 --- a/tests/components/powerwall/mocks.py +++ b/tests/components/powerwall/mocks.py @@ -16,17 +16,28 @@ async def _mock_powerwall_with_fixtures(hass): meters = await _async_load_json_fixture(hass, "meters.json") sitemaster = await _async_load_json_fixture(hass, "sitemaster.json") site_info = await _async_load_json_fixture(hass, "site_info.json") + status = await _async_load_json_fixture(hass, "status.json") + device_type = await _async_load_json_fixture(hass, "device_type.json") + return _mock_powerwall_return_value( site_info=site_info, charge=47.31993232, sitemaster=sitemaster, meters=meters, grid_status="SystemGridConnected", + status=status, + device_type=device_type, ) def _mock_powerwall_return_value( - site_info=None, charge=None, sitemaster=None, meters=None, grid_status=None + site_info=None, + charge=None, + sitemaster=None, + meters=None, + grid_status=None, + status=None, + device_type=None, ): powerwall_mock = MagicMock() type(powerwall_mock).site_info = PropertyMock(return_value=site_info) @@ -34,6 +45,8 @@ def _mock_powerwall_return_value( type(powerwall_mock).sitemaster = PropertyMock(return_value=sitemaster) type(powerwall_mock).meters = PropertyMock(return_value=meters) type(powerwall_mock).grid_status = PropertyMock(return_value=grid_status) + type(powerwall_mock).status = PropertyMock(return_value=status) + type(powerwall_mock).device_type = PropertyMock(return_value=device_type) return powerwall_mock diff --git a/tests/components/powerwall/test_sensor.py b/tests/components/powerwall/test_sensor.py index 090e5dac445..7f092683b7c 100644 --- a/tests/components/powerwall/test_sensor.py +++ b/tests/components/powerwall/test_sensor.py @@ -27,7 +27,8 @@ async def test_sensors(hass): identifiers={("powerwall", "Wom Energy_60Hz_240V_s_IEEE1547a_2014_13.5")}, connections=set(), ) - assert reg_device.model == "PowerWall 2" + assert reg_device.model == "PowerWall 2 (hec)" + assert reg_device.sw_version == "1.45.1" assert reg_device.manufacturer == "Tesla" assert reg_device.name == "MySite" diff --git a/tests/fixtures/powerwall/device_type.json b/tests/fixtures/powerwall/device_type.json new file mode 100644 index 00000000000..a94c047219e --- /dev/null +++ b/tests/fixtures/powerwall/device_type.json @@ -0,0 +1 @@ +{"device_type":"hec"} diff --git a/tests/fixtures/powerwall/status.json b/tests/fixtures/powerwall/status.json new file mode 100644 index 00000000000..41e0288b18d --- /dev/null +++ b/tests/fixtures/powerwall/status.json @@ -0,0 +1 @@ +{"start_time":"2020-03-10 11:57:25 +0800","up_time_seconds":"217h40m57.470801079s","is_new":false,"version":"1.45.1","git_hash":"13bf684a633175f884079ec79f42997080d90310"}