From 84e5b7350e9dd859228a3ec125c2759e447483b2 Mon Sep 17 00:00:00 2001 From: Tiago Rodrigues Date: Mon, 13 Feb 2023 13:09:14 +0000 Subject: [PATCH 1/9] Added service to start/stop charge --- homeassistant/components/renault/button.py | 6 +++ homeassistant/components/renault/services.py | 48 ++++++++++++++++++- .../components/renault/services.yaml | 11 +++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/renault/button.py b/homeassistant/components/renault/button.py index b34e14d365a4a9..7f00ad57765730 100644 --- a/homeassistant/components/renault/button.py +++ b/homeassistant/components/renault/button.py @@ -70,5 +70,11 @@ async def async_press(self) -> None: icon="mdi:ev-station", name="Start charge", requires_electricity=True, + ),RenaultButtonEntityDescription( + async_press=lambda x: x.vehicle.set_charge_stop(), + key="stop_charge", + icon="mdi:ev-station", + name="Stop charge", + requires_electricity=True, ), ) diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index d25b73cafc27c7..4d80ea060ba2ba 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -63,7 +63,9 @@ SERVICE_AC_CANCEL = "ac_cancel" SERVICE_AC_START = "ac_start" SERVICE_CHARGE_SET_SCHEDULES = "charge_set_schedules" -SERVICES = [SERVICE_AC_CANCEL, SERVICE_AC_START, SERVICE_CHARGE_SET_SCHEDULES] +SERVICE_CHARGE_START = "charge_start" +SERVICE_CHARGE_PAUSE = "charge_pause" +SERVICES = [SERVICE_AC_CANCEL, SERVICE_AC_START, SERVICE_CHARGE_SET_SCHEDULES, SERVICE_CHARGE_START, SERVICE_CHARGE_PAUSE] def setup_services(hass: HomeAssistant) -> None: @@ -87,6 +89,38 @@ async def ac_start(service_call: ServiceCall) -> None: result = await proxy.set_ac_start(temperature, when) LOGGER.debug("A/C start result: %s", result.raw_data) + async def charge_start(service_call: ServiceCall) -> None: + """Start charge.""" + # The Renault start charge service has been replaced by a + # dedicated button entity and marked as deprecated + LOGGER.warning( + "The 'renault.charge_start' service is deprecated and " + "replaced by a dedicated start charge button entity; please " + "use that entity to start the charge instead" + ) + + proxy = get_vehicle_proxy(service_call.data) + + LOGGER.debug("Charge start attempt") + result = await proxy.vehicle.set_charge_stop() + LOGGER.debug("Charge start result: %s", result) + + async def charge_pause(service_call: ServiceCall) -> None: + """Pause charge.""" + # The Renault pause charge service has been replaced by a + # dedicated button entity and marked as deprecated + LOGGER.warning( + "The 'renault.charge_pause' service is deprecated and " + "replaced by a dedicated start charge button entity; please " + "use that entity to start the charge instead" + ) + + proxy = get_vehicle_proxy(service_call.data) + + LOGGER.debug("Charge pause attempt") + result = await proxy.vehicle.set_charge_stop() + LOGGER.debug("Charge pause result: %s", result) + async def charge_set_schedules(service_call: ServiceCall) -> None: """Set charge schedules.""" schedules: list[dict[str, Any]] = service_call.data[ATTR_SCHEDULES] @@ -132,6 +166,18 @@ def get_vehicle_proxy(service_call_data: Mapping) -> RenaultVehicleProxy: ac_start, schema=SERVICE_AC_START_SCHEMA, ) + hass.services.async_register( + DOMAIN, + SERVICE_CHARGE_START, + charge_start, + schema=SERVICE_VEHICLE_SCHEMA, + ) + hass.services.async_register( + DOMAIN, + SERVICE_CHARGE_PAUSE, + charge_pause, + schema=SERVICE_VEHICLE_SCHEMA, + ) hass.services.async_register( DOMAIN, SERVICE_CHARGE_SET_SCHEDULES, diff --git a/homeassistant/components/renault/services.yaml b/homeassistant/components/renault/services.yaml index 7dd2f73ef4b19b..c9ae6ed0d7e0c6 100644 --- a/homeassistant/components/renault/services.yaml +++ b/homeassistant/components/renault/services.yaml @@ -86,3 +86,14 @@ charge_start: selector: device: integration: renault + +charge_pause: + description: Pause charge on vehicle. + fields: + vehicle: + name: Vehicle + description: The vehicle to send the command to. + required: true + selector: + device: + integration: renault From 561eac062647a1a22d207588feddd6d756aa22a6 Mon Sep 17 00:00:00 2001 From: Tiago Rodrigues Date: Mon, 13 Feb 2023 13:13:50 +0000 Subject: [PATCH 2/9] Remove comment --- homeassistant/components/renault/services.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index 4d80ea060ba2ba..8cf979838ad3e0 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -91,14 +91,6 @@ async def ac_start(service_call: ServiceCall) -> None: async def charge_start(service_call: ServiceCall) -> None: """Start charge.""" - # The Renault start charge service has been replaced by a - # dedicated button entity and marked as deprecated - LOGGER.warning( - "The 'renault.charge_start' service is deprecated and " - "replaced by a dedicated start charge button entity; please " - "use that entity to start the charge instead" - ) - proxy = get_vehicle_proxy(service_call.data) LOGGER.debug("Charge start attempt") @@ -107,14 +99,6 @@ async def charge_start(service_call: ServiceCall) -> None: async def charge_pause(service_call: ServiceCall) -> None: """Pause charge.""" - # The Renault pause charge service has been replaced by a - # dedicated button entity and marked as deprecated - LOGGER.warning( - "The 'renault.charge_pause' service is deprecated and " - "replaced by a dedicated start charge button entity; please " - "use that entity to start the charge instead" - ) - proxy = get_vehicle_proxy(service_call.data) LOGGER.debug("Charge pause attempt") From 473fc966a4d02398df248603a74e29742cd9114e Mon Sep 17 00:00:00 2001 From: Tiago Rodrigues Date: Mon, 13 Feb 2023 13:21:48 +0000 Subject: [PATCH 3/9] Fixed service --- homeassistant/components/renault/manifest.json | 3 ++- .../components/renault/renault_vehicle.py | 5 +++++ homeassistant/components/renault/services.py | 18 +++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/renault/manifest.json b/homeassistant/components/renault/manifest.json index 9fade49b4b4702..4875412f86142a 100644 --- a/homeassistant/components/renault/manifest.json +++ b/homeassistant/components/renault/manifest.json @@ -8,5 +8,6 @@ "iot_class": "cloud_polling", "loggers": ["renault_api"], "quality_scale": "platinum", - "requirements": ["renault-api==0.1.12"] + "requirements": ["renault-api==0.1.12"], + "version": "0.1" } diff --git a/homeassistant/components/renault/renault_vehicle.py b/homeassistant/components/renault/renault_vehicle.py index 69835552ba4cb2..9580ea2b7d0078 100644 --- a/homeassistant/components/renault/renault_vehicle.py +++ b/homeassistant/components/renault/renault_vehicle.py @@ -151,6 +151,11 @@ async def set_charge_start(self) -> models.KamereonVehicleChargingStartActionDat """Start vehicle charge.""" return await self._vehicle.set_charge_start() + @with_error_wrapping + async def set_charge_stop(self) -> models.KamereonVehicleChargingStartActionData: + """Stop vehicle charge.""" + return await self._vehicle.set_charge_stop() + @with_error_wrapping async def set_ac_stop(self) -> models.KamereonVehicleHvacStartActionData: """Stop vehicle ac.""" diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index 8cf979838ad3e0..4b85469b35cb8d 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -91,14 +91,30 @@ async def ac_start(service_call: ServiceCall) -> None: async def charge_start(service_call: ServiceCall) -> None: """Start charge.""" + # The Renault start charge service has been replaced by a + # dedicated button entity and marked as deprecated + LOGGER.warning( + "The 'renault.charge_start' service is deprecated and " + "replaced by a dedicated start charge button entity; please " + "use that entity to start the charge instead" + ) + proxy = get_vehicle_proxy(service_call.data) LOGGER.debug("Charge start attempt") - result = await proxy.vehicle.set_charge_stop() + result = await proxy.vehicle.set_charge_start() LOGGER.debug("Charge start result: %s", result) async def charge_pause(service_call: ServiceCall) -> None: """Pause charge.""" + # The Renault pause charge service has been replaced by a + # dedicated button entity and marked as deprecated + LOGGER.warning( + "The 'renault.charge_pause' service is deprecated and " + "replaced by a dedicated start charge button entity; please " + "use that entity to start the charge instead" + ) + proxy = get_vehicle_proxy(service_call.data) LOGGER.debug("Charge pause attempt") From 74d08ec90deed760fd8bfdf14a005c775485ced4 Mon Sep 17 00:00:00 2001 From: Tiago Rodrigues Date: Mon, 13 Feb 2023 13:55:17 +0000 Subject: [PATCH 4/9] removed service for start/stop charge --- homeassistant/components/renault/services.py | 48 +------------------ .../components/renault/services.yaml | 24 +--------- 2 files changed, 2 insertions(+), 70 deletions(-) diff --git a/homeassistant/components/renault/services.py b/homeassistant/components/renault/services.py index 4b85469b35cb8d..d25b73cafc27c7 100644 --- a/homeassistant/components/renault/services.py +++ b/homeassistant/components/renault/services.py @@ -63,9 +63,7 @@ SERVICE_AC_CANCEL = "ac_cancel" SERVICE_AC_START = "ac_start" SERVICE_CHARGE_SET_SCHEDULES = "charge_set_schedules" -SERVICE_CHARGE_START = "charge_start" -SERVICE_CHARGE_PAUSE = "charge_pause" -SERVICES = [SERVICE_AC_CANCEL, SERVICE_AC_START, SERVICE_CHARGE_SET_SCHEDULES, SERVICE_CHARGE_START, SERVICE_CHARGE_PAUSE] +SERVICES = [SERVICE_AC_CANCEL, SERVICE_AC_START, SERVICE_CHARGE_SET_SCHEDULES] def setup_services(hass: HomeAssistant) -> None: @@ -89,38 +87,6 @@ async def ac_start(service_call: ServiceCall) -> None: result = await proxy.set_ac_start(temperature, when) LOGGER.debug("A/C start result: %s", result.raw_data) - async def charge_start(service_call: ServiceCall) -> None: - """Start charge.""" - # The Renault start charge service has been replaced by a - # dedicated button entity and marked as deprecated - LOGGER.warning( - "The 'renault.charge_start' service is deprecated and " - "replaced by a dedicated start charge button entity; please " - "use that entity to start the charge instead" - ) - - proxy = get_vehicle_proxy(service_call.data) - - LOGGER.debug("Charge start attempt") - result = await proxy.vehicle.set_charge_start() - LOGGER.debug("Charge start result: %s", result) - - async def charge_pause(service_call: ServiceCall) -> None: - """Pause charge.""" - # The Renault pause charge service has been replaced by a - # dedicated button entity and marked as deprecated - LOGGER.warning( - "The 'renault.charge_pause' service is deprecated and " - "replaced by a dedicated start charge button entity; please " - "use that entity to start the charge instead" - ) - - proxy = get_vehicle_proxy(service_call.data) - - LOGGER.debug("Charge pause attempt") - result = await proxy.vehicle.set_charge_stop() - LOGGER.debug("Charge pause result: %s", result) - async def charge_set_schedules(service_call: ServiceCall) -> None: """Set charge schedules.""" schedules: list[dict[str, Any]] = service_call.data[ATTR_SCHEDULES] @@ -166,18 +132,6 @@ def get_vehicle_proxy(service_call_data: Mapping) -> RenaultVehicleProxy: ac_start, schema=SERVICE_AC_START_SCHEMA, ) - hass.services.async_register( - DOMAIN, - SERVICE_CHARGE_START, - charge_start, - schema=SERVICE_VEHICLE_SCHEMA, - ) - hass.services.async_register( - DOMAIN, - SERVICE_CHARGE_PAUSE, - charge_pause, - schema=SERVICE_VEHICLE_SCHEMA, - ) hass.services.async_register( DOMAIN, SERVICE_CHARGE_SET_SCHEDULES, diff --git a/homeassistant/components/renault/services.yaml b/homeassistant/components/renault/services.yaml index c9ae6ed0d7e0c6..d4a2a5059dc6ae 100644 --- a/homeassistant/components/renault/services.yaml +++ b/homeassistant/components/renault/services.yaml @@ -74,26 +74,4 @@ charge_set_schedules: ] required: true selector: - object: - -charge_start: - description: Start charge on vehicle. - fields: - vehicle: - name: Vehicle - description: The vehicle to send the command to. - required: true - selector: - device: - integration: renault - -charge_pause: - description: Pause charge on vehicle. - fields: - vehicle: - name: Vehicle - description: The vehicle to send the command to. - required: true - selector: - device: - integration: renault + object: \ No newline at end of file From 142fd148275a3015ba5dc954dbe33127d3a255ab Mon Sep 17 00:00:00 2001 From: rodriguestiago0 Date: Mon, 13 Feb 2023 13:59:25 +0000 Subject: [PATCH 5/9] Remove version Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/renault/manifest.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/renault/manifest.json b/homeassistant/components/renault/manifest.json index 4875412f86142a..9fade49b4b4702 100644 --- a/homeassistant/components/renault/manifest.json +++ b/homeassistant/components/renault/manifest.json @@ -8,6 +8,5 @@ "iot_class": "cloud_polling", "loggers": ["renault_api"], "quality_scale": "platinum", - "requirements": ["renault-api==0.1.12"], - "version": "0.1" + "requirements": ["renault-api==0.1.12"] } From c1252d68ae6db6b6b609ffecebcb67c5167fad3a Mon Sep 17 00:00:00 2001 From: rodriguestiago0 Date: Mon, 13 Feb 2023 13:59:34 +0000 Subject: [PATCH 6/9] Format Co-authored-by: epenet <6771947+epenet@users.noreply.github.com> --- homeassistant/components/renault/button.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/renault/button.py b/homeassistant/components/renault/button.py index 7f00ad57765730..67dfe8fc971eab 100644 --- a/homeassistant/components/renault/button.py +++ b/homeassistant/components/renault/button.py @@ -70,7 +70,8 @@ async def async_press(self) -> None: icon="mdi:ev-station", name="Start charge", requires_electricity=True, - ),RenaultButtonEntityDescription( + ), + RenaultButtonEntityDescription( async_press=lambda x: x.vehicle.set_charge_stop(), key="stop_charge", icon="mdi:ev-station", From 3d820289a661b043e4eedc475adb0c208344b09e Mon Sep 17 00:00:00 2001 From: Tiago Rodrigues Date: Mon, 13 Feb 2023 14:04:53 +0000 Subject: [PATCH 7/9] Revert change --- homeassistant/components/renault/services.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/renault/services.yaml b/homeassistant/components/renault/services.yaml index d4a2a5059dc6ae..052c44f3576689 100644 --- a/homeassistant/components/renault/services.yaml +++ b/homeassistant/components/renault/services.yaml @@ -74,4 +74,15 @@ charge_set_schedules: ] required: true selector: - object: \ No newline at end of file + object: + +charge_start: + description: Start charge on vehicle. + fields: + vehicle: + name: Vehicle + description: The vehicle to send the command to. + required: true + selector: + device: + integration: renault \ No newline at end of file From ed896fbf36b4931b6422c58ba4ca10deaa652942 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 14 Feb 2023 09:13:37 +0000 Subject: [PATCH 8/9] Fix lint --- homeassistant/components/renault/services.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/renault/services.yaml b/homeassistant/components/renault/services.yaml index 052c44f3576689..7dd2f73ef4b19b 100644 --- a/homeassistant/components/renault/services.yaml +++ b/homeassistant/components/renault/services.yaml @@ -85,4 +85,4 @@ charge_start: required: true selector: device: - integration: renault \ No newline at end of file + integration: renault From 86aff2dd417413cab8a2b353a446a382b92d5ce3 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Tue, 14 Feb 2023 09:30:09 +0000 Subject: [PATCH 9/9] Add tests --- tests/components/renault/const.py | 18 ++++++++++++ .../fixtures/action.set_charge_stop.json | 7 +++++ tests/components/renault/test_button.py | 28 +++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 tests/components/renault/fixtures/action.set_charge_stop.json diff --git a/tests/components/renault/const.py b/tests/components/renault/const.py index ee4f1683aad3eb..e17e6b64948131 100644 --- a/tests/components/renault/const.py +++ b/tests/components/renault/const.py @@ -114,6 +114,12 @@ ATTR_STATE: STATE_UNKNOWN, ATTR_UNIQUE_ID: "vf1aaaaa555777999_start_charge", }, + { + ATTR_ENTITY_ID: "button.reg_number_stop_charge", + ATTR_ICON: "mdi:ev-station", + ATTR_STATE: STATE_UNKNOWN, + ATTR_UNIQUE_ID: "vf1aaaaa555777999_stop_charge", + }, ], Platform.DEVICE_TRACKER: [], Platform.SELECT: [ @@ -336,6 +342,12 @@ ATTR_STATE: STATE_UNKNOWN, ATTR_UNIQUE_ID: "vf1aaaaa555777999_start_charge", }, + { + ATTR_ENTITY_ID: "button.reg_number_stop_charge", + ATTR_ICON: "mdi:ev-station", + ATTR_STATE: STATE_UNKNOWN, + ATTR_UNIQUE_ID: "vf1aaaaa555777999_stop_charge", + }, ], Platform.DEVICE_TRACKER: [ { @@ -565,6 +577,12 @@ ATTR_STATE: STATE_UNKNOWN, ATTR_UNIQUE_ID: "vf1aaaaa555777123_start_charge", }, + { + ATTR_ENTITY_ID: "button.reg_number_stop_charge", + ATTR_ICON: "mdi:ev-station", + ATTR_STATE: STATE_UNKNOWN, + ATTR_UNIQUE_ID: "vf1aaaaa555777123_stop_charge", + }, ], Platform.DEVICE_TRACKER: [ { diff --git a/tests/components/renault/fixtures/action.set_charge_stop.json b/tests/components/renault/fixtures/action.set_charge_stop.json new file mode 100644 index 00000000000000..017059782b625d --- /dev/null +++ b/tests/components/renault/fixtures/action.set_charge_stop.json @@ -0,0 +1,7 @@ +{ + "data": { + "type": "ChargingStart", + "id": "guid", + "attributes": { "action": "stop" } + } +} diff --git a/tests/components/renault/test_button.py b/tests/components/renault/test_button.py index 8ec18e3b101e43..695be73089ef87 100644 --- a/tests/components/renault/test_button.py +++ b/tests/components/renault/test_button.py @@ -160,6 +160,34 @@ async def test_button_start_charge( assert mock_action.mock_calls[0][1] == () +@pytest.mark.usefixtures("fixtures_with_data") +@pytest.mark.parametrize("vehicle_type", ["zoe_40"], indirect=True) +async def test_button_stop_charge( + hass: HomeAssistant, config_entry: ConfigEntry +) -> None: + """Test that button invokes renault_api with correct data.""" + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + data = { + ATTR_ENTITY_ID: "button.reg_number_stop_charge", + } + + with patch( + "renault_api.renault_vehicle.RenaultVehicle.set_charge_stop", + return_value=( + schemas.KamereonVehicleChargingStartActionDataSchema.loads( + load_fixture("renault/action.set_charge_stop.json") + ) + ), + ) as mock_action: + await hass.services.async_call( + BUTTON_DOMAIN, SERVICE_PRESS, service_data=data, blocking=True + ) + assert len(mock_action.mock_calls) == 1 + assert mock_action.mock_calls[0][1] == () + + @pytest.mark.usefixtures("fixtures_with_data") @pytest.mark.parametrize("vehicle_type", ["zoe_40"], indirect=True) async def test_button_start_air_conditioner(