diff --git a/CODEOWNERS b/CODEOWNERS index ab4457e0da6d..281cf4fbecb0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -824,8 +824,8 @@ CLAUDE.md @home-assistant/core /tests/components/hue/ @marcelveldt /homeassistant/components/hue_ble/ @flip-dots /tests/components/hue_ble/ @flip-dots -/homeassistant/components/huisbaasje/ @dennisschroer -/tests/components/huisbaasje/ @dennisschroer +/homeassistant/components/huisbaasje/ @dennisschroer @DieEneSchrodinger +/tests/components/huisbaasje/ @dennisschroer @DieEneSchrodinger /homeassistant/components/humidifier/ @home-assistant/core @Shulyaka /tests/components/humidifier/ @home-assistant/core @Shulyaka /homeassistant/components/humidity/ @home-assistant/core diff --git a/homeassistant/components/huisbaasje/__init__.py b/homeassistant/components/huisbaasje/__init__.py index 7eca8141dc3c..e11d1d23fc45 100644 --- a/homeassistant/components/huisbaasje/__init__.py +++ b/homeassistant/components/huisbaasje/__init__.py @@ -32,6 +32,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: EnergyFlipConfigEntry) - _LOGGER.error("Authentication failed: %s", str(exception)) return False + # Immediately get customer id, since it is required for all api calls + try: + await energyflip.customer_overview() + except EnergyFlipException as exception: + _LOGGER.error("Getting customer id failed: %s", str(exception)) + return False + # Create a coordinator for polling updates coordinator = EnergyFlipUpdateCoordinator(hass, entry, energyflip) diff --git a/homeassistant/components/huisbaasje/const.py b/homeassistant/components/huisbaasje/const.py index a2dc39cb565e..746461b253a3 100644 --- a/homeassistant/components/huisbaasje/const.py +++ b/homeassistant/components/huisbaasje/const.py @@ -3,8 +3,10 @@ from energyflip.const import ( SOURCE_TYPE_ELECTRICITY, SOURCE_TYPE_ELECTRICITY_IN, + SOURCE_TYPE_ELECTRICITY_IN_HIGH, SOURCE_TYPE_ELECTRICITY_IN_LOW, SOURCE_TYPE_ELECTRICITY_OUT, + SOURCE_TYPE_ELECTRICITY_OUT_HIGH, SOURCE_TYPE_ELECTRICITY_OUT_LOW, SOURCE_TYPE_GAS, ) @@ -27,7 +29,9 @@ SOURCE_TYPES = [ SOURCE_TYPE_ELECTRICITY, SOURCE_TYPE_ELECTRICITY_IN, SOURCE_TYPE_ELECTRICITY_IN_LOW, + SOURCE_TYPE_ELECTRICITY_IN_HIGH, SOURCE_TYPE_ELECTRICITY_OUT, SOURCE_TYPE_ELECTRICITY_OUT_LOW, + SOURCE_TYPE_ELECTRICITY_OUT_HIGH, SOURCE_TYPE_GAS, ] diff --git a/homeassistant/components/huisbaasje/coordinator.py b/homeassistant/components/huisbaasje/coordinator.py index 362d3edfe580..301aaec2a09f 100644 --- a/homeassistant/components/huisbaasje/coordinator.py +++ b/homeassistant/components/huisbaasje/coordinator.py @@ -59,10 +59,7 @@ class EnergyFlipUpdateCoordinator(DataUpdateCoordinator[dict[str, dict[str, Any] # Note: TimeoutError and aiohttp.ClientError are already # handled by the data update coordinator. async with asyncio.timeout(FETCH_TIMEOUT): - if not self._energyflip.is_authenticated(): - _LOGGER.warning("EnergyFlip is unauthenticated. Reauthenticating") - await self._energyflip.authenticate() - + # Invalidated authentications are handled by current_measurements itself current_measurements = await self._energyflip.current_measurements() return { diff --git a/homeassistant/components/huisbaasje/manifest.json b/homeassistant/components/huisbaasje/manifest.json index 33e58bf4eb66..6ec844139512 100644 --- a/homeassistant/components/huisbaasje/manifest.json +++ b/homeassistant/components/huisbaasje/manifest.json @@ -1,11 +1,11 @@ { "domain": "huisbaasje", "name": "EnergyFlip", - "codeowners": ["@dennisschroer"], + "codeowners": ["@dennisschroer", "@DieEneSchrodinger"], "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/huisbaasje", "integration_type": "device", "iot_class": "cloud_polling", "loggers": ["energyflip"], - "requirements": ["energyflip-client==0.2.2"] + "requirements": ["energyflip-api-client==0.3.0"] } diff --git a/homeassistant/components/huisbaasje/sensor.py b/homeassistant/components/huisbaasje/sensor.py index 63b6f93229af..191099f4dc18 100644 --- a/homeassistant/components/huisbaasje/sensor.py +++ b/homeassistant/components/huisbaasje/sensor.py @@ -59,38 +59,6 @@ SENSORS_INFO = [ key=SOURCE_TYPE_ELECTRICITY, state_class=SensorStateClass.MEASUREMENT, ), - EnergyFlipSensorEntityDescription( - translation_key="current_power_peak", - sensor_type=SENSOR_TYPE_RATE, - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.WATT, - key=SOURCE_TYPE_ELECTRICITY_IN, - state_class=SensorStateClass.MEASUREMENT, - ), - EnergyFlipSensorEntityDescription( - translation_key="current_power_off_peak", - sensor_type=SENSOR_TYPE_RATE, - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.WATT, - key=SOURCE_TYPE_ELECTRICITY_IN_LOW, - state_class=SensorStateClass.MEASUREMENT, - ), - EnergyFlipSensorEntityDescription( - translation_key="current_power_out_peak", - sensor_type=SENSOR_TYPE_RATE, - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.WATT, - key=SOURCE_TYPE_ELECTRICITY_OUT, - state_class=SensorStateClass.MEASUREMENT, - ), - EnergyFlipSensorEntityDescription( - translation_key="current_power_out_off_peak", - sensor_type=SENSOR_TYPE_RATE, - device_class=SensorDeviceClass.POWER, - native_unit_of_measurement=UnitOfPower.WATT, - key=SOURCE_TYPE_ELECTRICITY_OUT_LOW, - state_class=SensorStateClass.MEASUREMENT, - ), EnergyFlipSensorEntityDescription( translation_key="energy_consumption_peak_today", device_class=SensorDeviceClass.ENERGY, diff --git a/homeassistant/components/huisbaasje/strings.json b/homeassistant/components/huisbaasje/strings.json index ab2a90636085..16389de6c664 100644 --- a/homeassistant/components/huisbaasje/strings.json +++ b/homeassistant/components/huisbaasje/strings.json @@ -25,18 +25,6 @@ "current_power": { "name": "Current power" }, - "current_power_off_peak": { - "name": "Current power in off-peak" - }, - "current_power_out_off_peak": { - "name": "Current power out off-peak" - }, - "current_power_out_peak": { - "name": "Current power out peak" - }, - "current_power_peak": { - "name": "Current power in peak" - }, "energy_consumption_off_peak_today": { "name": "Energy consumption off-peak today" }, diff --git a/requirements_all.txt b/requirements_all.txt index 481ff86b2486..0f3243b94c07 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -950,7 +950,7 @@ emulated-roku==0.3.0 energieleser==0.1.6 # homeassistant.components.huisbaasje -energyflip-client==0.2.2 +energyflip-api-client==0.3.0 # homeassistant.components.energyid energyid-webhooks==0.0.14 diff --git a/tests/components/huisbaasje/test_init.py b/tests/components/huisbaasje/test_init.py index 245cde5e9afa..d7274d505021 100644 --- a/tests/components/huisbaasje/test_init.py +++ b/tests/components/huisbaasje/test_init.py @@ -20,13 +20,14 @@ async def test_setup_entry(hass: HomeAssistant) -> None: patch( "energyflip.EnergyFlip.authenticate", return_value=None ) as mock_authenticate, - patch( - "energyflip.EnergyFlip.is_authenticated", return_value=True - ) as mock_is_authenticated, patch( "energyflip.EnergyFlip.current_measurements", return_value=MOCK_CURRENT_MEASUREMENTS, ) as mock_current_measurements, + patch( + "energyflip.EnergyFlip.customer_overview", + return_value=None, + ) as mock_customer_overview, ): config_entry = MockConfigEntry( version=1, @@ -50,19 +51,25 @@ async def test_setup_entry(hass: HomeAssistant) -> None: # Assert entities are loaded entities = hass.states.async_entity_ids("sensor") - assert len(entities) == 18 + assert len(entities) == 14 # Assert mocks are called assert len(mock_authenticate.mock_calls) == 1 - assert len(mock_is_authenticated.mock_calls) == 1 assert len(mock_current_measurements.mock_calls) == 1 + assert len(mock_customer_overview.mock_calls) == 1 -async def test_setup_entry_error(hass: HomeAssistant) -> None: - """Test for successfully setting a config entry.""" - with patch( - "energyflip.EnergyFlip.authenticate", side_effect=EnergyFlipException - ) as mock_authenticate: +async def test_setup_entry_error_authenticate(hass: HomeAssistant) -> None: + """Test for unsuccessfully setting a config entry, erroring on authenticate.""" + with ( + patch( + "energyflip.EnergyFlip.authenticate", side_effect=EnergyFlipException + ) as mock_authenticate, + patch( + "energyflip.EnergyFlip.customer_overview", + side_effect=EnergyFlipException, + ) as mock_customer_overview, + ): config_entry = MockConfigEntry( version=1, domain=DOMAIN, @@ -91,6 +98,50 @@ async def test_setup_entry_error(hass: HomeAssistant) -> None: # Assert mocks are called assert len(mock_authenticate.mock_calls) == 1 + # Assert customer_overview wasn't called and authenticate exception was handled properly + assert len(mock_customer_overview.mock_calls) == 0 + + +async def test_setup_entry_error_customer_overview(hass: HomeAssistant) -> None: + """Test for unsuccessfully setting a config entry, erroring on customer_overview.""" + with ( + patch( + "energyflip.EnergyFlip.authenticate", return_value=None + ) as mock_authenticate, + patch( + "energyflip.EnergyFlip.customer_overview", + side_effect=EnergyFlipException, + ) as mock_customer_overview, + ): + config_entry = MockConfigEntry( + version=1, + domain=DOMAIN, + title="userId", + data={ + CONF_ID: "userId", + CONF_USERNAME: "username", + CONF_PASSWORD: "password", + }, + source="test", + ) + config_entry.add_to_hass(hass) + + assert config_entry.state is ConfigEntryState.NOT_LOADED + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + # Assert integration is loaded with error + assert config_entry.state is ConfigEntryState.SETUP_ERROR + assert DOMAIN not in hass.data + + # Assert entities are not loaded + entities = hass.states.async_entity_ids("sensor") + assert len(entities) == 0 + + # Assert mocks are called + assert len(mock_authenticate.mock_calls) == 1 + assert len(mock_customer_overview.mock_calls) == 1 + async def test_unload_entry(hass: HomeAssistant) -> None: """Test for successfully unloading the config entry.""" @@ -98,13 +149,14 @@ async def test_unload_entry(hass: HomeAssistant) -> None: patch( "energyflip.EnergyFlip.authenticate", return_value=None ) as mock_authenticate, - patch( - "energyflip.EnergyFlip.is_authenticated", return_value=True - ) as mock_is_authenticated, patch( "energyflip.EnergyFlip.current_measurements", return_value=MOCK_CURRENT_MEASUREMENTS, ) as mock_current_measurements, + patch( + "energyflip.EnergyFlip.customer_overview", + return_value=None, + ) as mock_customer_overview, ): config_entry = MockConfigEntry( version=1, @@ -124,13 +176,13 @@ async def test_unload_entry(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert config_entry.state is ConfigEntryState.LOADED entities = hass.states.async_entity_ids("sensor") - assert len(entities) == 18 + assert len(entities) == 14 # Unload config entry await hass.config_entries.async_unload(config_entry.entry_id) assert config_entry.state is ConfigEntryState.NOT_LOADED entities = hass.states.async_entity_ids("sensor") - assert len(entities) == 18 + assert len(entities) == 14 for entity in entities: assert hass.states.get(entity).state == STATE_UNAVAILABLE @@ -142,5 +194,5 @@ async def test_unload_entry(hass: HomeAssistant) -> None: # Assert mocks are called assert len(mock_authenticate.mock_calls) == 1 - assert len(mock_is_authenticated.mock_calls) == 1 assert len(mock_current_measurements.mock_calls) == 1 + assert len(mock_customer_overview.mock_calls) == 1 diff --git a/tests/components/huisbaasje/test_sensor.py b/tests/components/huisbaasje/test_sensor.py index 5958d6bceea8..71cc5b5d379f 100644 --- a/tests/components/huisbaasje/test_sensor.py +++ b/tests/components/huisbaasje/test_sensor.py @@ -32,13 +32,14 @@ async def test_setup_entry(hass: HomeAssistant) -> None: patch( "energyflip.EnergyFlip.authenticate", return_value=None ) as mock_authenticate, - patch( - "energyflip.EnergyFlip.is_authenticated", return_value=True - ) as mock_is_authenticated, patch( "energyflip.EnergyFlip.current_measurements", return_value=MOCK_CURRENT_MEASUREMENTS, ) as mock_current_measurements, + patch( + "energyflip.EnergyFlip.customer_overview", + return_value=None, + ) as mock_customer_overview, ): config_entry = MockConfigEntry( version=1, @@ -70,66 +71,6 @@ async def test_setup_entry(hass: HomeAssistant) -> None: current_power.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UnitOfPower.WATT ) - current_power_in = hass.states.get("sensor.current_power_in_peak") - assert current_power_in.state == "1011.66666666667" - assert ( - current_power_in.attributes.get(ATTR_DEVICE_CLASS) - == SensorDeviceClass.POWER - ) - assert ( - current_power_in.attributes.get(ATTR_STATE_CLASS) - is SensorStateClass.MEASUREMENT - ) - assert ( - current_power_in.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - == UnitOfPower.WATT - ) - - current_power_in_low = hass.states.get("sensor.current_power_in_off_peak") - assert current_power_in_low.state == "unknown" - assert ( - current_power_in_low.attributes.get(ATTR_DEVICE_CLASS) - == SensorDeviceClass.POWER - ) - assert ( - current_power_in_low.attributes.get(ATTR_STATE_CLASS) - is SensorStateClass.MEASUREMENT - ) - assert ( - current_power_in_low.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - == UnitOfPower.WATT - ) - - current_power_out = hass.states.get("sensor.current_power_out_peak") - assert current_power_out.state == "unknown" - assert ( - current_power_out.attributes.get(ATTR_DEVICE_CLASS) - == SensorDeviceClass.POWER - ) - assert ( - current_power_out.attributes.get(ATTR_STATE_CLASS) - is SensorStateClass.MEASUREMENT - ) - assert ( - current_power_out.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - == UnitOfPower.WATT - ) - - current_power_out_low = hass.states.get("sensor.current_power_out_off_peak") - assert current_power_out_low.state == "unknown" - assert ( - current_power_out_low.attributes.get(ATTR_DEVICE_CLASS) - == SensorDeviceClass.POWER - ) - assert ( - current_power_out_low.attributes.get(ATTR_STATE_CLASS) - is SensorStateClass.MEASUREMENT - ) - assert ( - current_power_out_low.attributes.get(ATTR_UNIT_OF_MEASUREMENT) - == UnitOfPower.WATT - ) - energy_consumption_peak_today = hass.states.get( "sensor.energy_consumption_peak_today" ) @@ -312,8 +253,8 @@ async def test_setup_entry(hass: HomeAssistant) -> None: # Assert mocks are called assert len(mock_authenticate.mock_calls) == 1 - assert len(mock_is_authenticated.mock_calls) == 1 assert len(mock_current_measurements.mock_calls) == 1 + assert len(mock_customer_overview.mock_calls) == 1 async def test_setup_entry_absent_measurement(hass: HomeAssistant) -> None: @@ -323,8 +264,9 @@ async def test_setup_entry_absent_measurement(hass: HomeAssistant) -> None: "energyflip.EnergyFlip.authenticate", return_value=None ) as mock_authenticate, patch( - "energyflip.EnergyFlip.is_authenticated", return_value=True - ) as mock_is_authenticated, + "energyflip.EnergyFlip.customer_overview", + return_value=None, + ) as mock_customer_overview, patch( "energyflip.EnergyFlip.current_measurements", return_value=MOCK_LIMITED_CURRENT_MEASUREMENTS, @@ -348,10 +290,6 @@ async def test_setup_entry_absent_measurement(hass: HomeAssistant) -> None: # Assert data is loaded assert hass.states.get("sensor.current_power").state == "1011.66666666667" - assert hass.states.get("sensor.current_power_in_peak").state == "unknown" - assert hass.states.get("sensor.current_power_in_off_peak").state == "unknown" - assert hass.states.get("sensor.current_power_out_peak").state == "unknown" - assert hass.states.get("sensor.current_power_out_off_peak").state == "unknown" assert hass.states.get("sensor.current_gas").state == "unknown" assert hass.states.get("sensor.energy_today").state == "3.296665869" assert ( @@ -370,5 +308,5 @@ async def test_setup_entry_absent_measurement(hass: HomeAssistant) -> None: # Assert mocks are called assert len(mock_authenticate.mock_calls) == 1 - assert len(mock_is_authenticated.mock_calls) == 1 assert len(mock_current_measurements.mock_calls) == 1 + assert len(mock_customer_overview.mock_calls) == 1