From de8aef36d0834ec542cddf3db200410c3897a9b6 Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:15:51 +0100 Subject: [PATCH 1/6] Bump version of Home Assistant to 2025.1.0 and change STATE_ to VacuumActivity. --- custom_components/purei9/manifest.json | 2 +- custom_components/purei9/purei9.py | 37 +++++++++++--------------- custom_components/purei9/vacuum.py | 19 ++++++------- requirements.txt | 2 +- 4 files changed, 26 insertions(+), 34 deletions(-) diff --git a/custom_components/purei9/manifest.json b/custom_components/purei9/manifest.json index 22a8fd5..5dd9436 100644 --- a/custom_components/purei9/manifest.json +++ b/custom_components/purei9/manifest.json @@ -9,5 +9,5 @@ "requirements": ["purei9_unofficial==0.0.16"], "iot_class": "cloud_polling", "config_flow": true, - "homeassistant": "2024.1.0" + "homeassistant": "2025.1.0" } diff --git a/custom_components/purei9/purei9.py b/custom_components/purei9/purei9.py index 6329705..0fc744b 100644 --- a/custom_components/purei9/purei9.py +++ b/custom_components/purei9/purei9.py @@ -10,31 +10,26 @@ ) from purei9_unofficial.cloudv3 import CloudZone, CloudMap from homeassistant.components.vacuum import ( - STATE_CLEANING, - STATE_DOCKED, - STATE_ERROR, - STATE_IDLE, - STATE_PAUSED, - STATE_RETURNING + VacuumActivity ) from . import const # See: https://github.com/Phype/purei9_unofficial/blob/master/src/purei9_unofficial/common.py PURE_I9_STATE_MAP = { - RobotStates.Cleaning: STATE_CLEANING, - RobotStates.Paused_Cleaning: STATE_PAUSED, - RobotStates.Spot_Cleaning: STATE_CLEANING, - RobotStates.Paused_Spot_Cleaning: STATE_PAUSED, - RobotStates.Return: STATE_RETURNING, - RobotStates.Paused_Return: STATE_PAUSED, - RobotStates.Return_for_Pitstop: STATE_RETURNING, - RobotStates.Paused_Return_for_Pitstop: STATE_PAUSED, - RobotStates.Charging: STATE_DOCKED, + RobotStates.Cleaning: VacuumActivity.CLEANING, + RobotStates.Paused_Cleaning: VacuumActivity.PAUSED, + RobotStates.Spot_Cleaning: VacuumActivity.CLEANING, + RobotStates.Paused_Spot_Cleaning: VacuumActivity.PAUSED, + RobotStates.Return: VacuumActivity.RETURNING, + RobotStates.Paused_Return: VacuumActivity.PAUSED, + RobotStates.Return_for_Pitstop: VacuumActivity.RETURNING, + RobotStates.Paused_Return_for_Pitstop: VacuumActivity.PAUSED, + RobotStates.Charging: VacuumActivity.DOCKED, # RobotStates.Sleeping: Special case, see function, - RobotStates.Error: STATE_ERROR, - RobotStates.Pitstop: STATE_DOCKED, + RobotStates.Error: VacuumActivity.ERROR, + RobotStates.Pitstop: VacuumActivity.DOCKED, # RobotStates.Manual_Steering: Manual steering?, - RobotStates.Firmware_Upgrade: STATE_DOCKED + RobotStates.Firmware_Upgrade: VacuumActivity.DOCKED } def state_to_hass( @@ -46,9 +41,9 @@ def state_to_hass( # In order to detect if it's docket or if it's just idling in the middle of a room # check the battery level. If it's full then we're docked. if pure_i9_state == RobotStates.Sleeping: - return STATE_DOCKED if pure_i9_battery == BatteryStatus.High else STATE_IDLE + return VacuumActivity.DOCKED if pure_i9_battery == BatteryStatus.High else VacuumActivity.IDLE - return PURE_I9_STATE_MAP.get(pure_i9_state, STATE_IDLE) + return PURE_I9_STATE_MAP.get(pure_i9_state, VacuumActivity.IDLE) # See: https://github.com/Phype/purei9_unofficial/blob/master/src/purei9_unofficial/common.py PURE_I9_BATTERY_MAP = { @@ -106,7 +101,7 @@ def params_map_create(cloud_map: CloudMap) -> ParamsMap: class Params: """Data available in the state""" battery: int = 100 - state: str = STATE_IDLE + state: str = VacuumActivity.IDLE available: bool = True firmware: str = None fan_speed: str = POWER_MODE_POWER diff --git a/custom_components/purei9/vacuum.py b/custom_components/purei9/vacuum.py index 6de5a7c..b8e31ce 100644 --- a/custom_components/purei9/vacuum.py +++ b/custom_components/purei9/vacuum.py @@ -6,10 +6,7 @@ from homeassistant.components.vacuum import ( StateVacuumEntity, PLATFORM_SCHEMA, - STATE_CLEANING, - STATE_PAUSED, - STATE_RETURNING, - STATE_IDLE, + VacuumActivity, VacuumEntityFeature ) from homeassistant.helpers.update_coordinator import CoordinatorEntity @@ -132,15 +129,15 @@ async def async_start(self): # If you click on start after clicking return, it will continue # returning. So we'll need to call stop first, then start in order # to start a clean. - if self._params.state == STATE_RETURNING: + if self._params.state == VacuumActivity.RETURNING: await self.hass.async_add_executor_job(self._robot.stopclean) # According to Home Assistant, pause should be an idempotent action. # However, the Pure i9 will toggle pause on/off if called multiple # times. Circumvent that. - if self._params.state != STATE_CLEANING: + if self._params.state != VacuumActivity.CLEANING: await self.hass.async_add_executor_job(self._robot.startclean) - self._params.state = STATE_CLEANING + self._params.state = VacuumActivity.CLEANING self.async_write_ha_state() def start(self): @@ -149,7 +146,7 @@ def start(self): async def async_return_to_base(self, **kwargs): """Return to the dock""" await self.hass.async_add_executor_job(self._robot.gohome) - self._params.state = STATE_RETURNING + self._params.state = VacuumActivity.RETURNING self.async_write_ha_state() def return_to_base(self, **kwargs): @@ -158,7 +155,7 @@ def return_to_base(self, **kwargs): async def async_stop(self, **kwargs): """Stop cleaning""" await self.hass.async_add_executor_job(self._robot.stopclean) - self._params.state = STATE_IDLE + self._params.state = VacuumActivity.IDLE self.async_write_ha_state() def stop(self, **kwargs): @@ -169,9 +166,9 @@ async def async_pause(self): # According to Home Assistant, pause should be an idempotent # action. However, the Pure i9 will toggle pause on/off if # called multiple times. Circumvent that. - if self._params.state != STATE_PAUSED: + if self._params.state != VacuumActivity.PAUSED: await self.hass.async_add_executor_job(self._robot.pauseclean) - self._params.state = STATE_PAUSED + self._params.state = VacuumActivity.PAUSED self.async_write_ha_state() def pause(self): diff --git a/requirements.txt b/requirements.txt index 1685fae..c2e8896 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ purei9_unofficial==0.0.16 -homeassistant==2024.1.0 +homeassistant==2025.1.0 pylint==3.3.1 From 6f84205e48d6b3d6da9ee1229c183788fc19a78f Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:33:14 +0100 Subject: [PATCH 2/6] Implement activity as parameter instead of setting state directly --- custom_components/purei9/purei9.py | 2 +- custom_components/purei9/vacuum.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/custom_components/purei9/purei9.py b/custom_components/purei9/purei9.py index 0fc744b..4408e90 100644 --- a/custom_components/purei9/purei9.py +++ b/custom_components/purei9/purei9.py @@ -101,7 +101,7 @@ def params_map_create(cloud_map: CloudMap) -> ParamsMap: class Params: """Data available in the state""" battery: int = 100 - state: str = VacuumActivity.IDLE + state: VacuumActivity = VacuumActivity.IDLE available: bool = True firmware: str = None fan_speed: str = POWER_MODE_POWER diff --git a/custom_components/purei9/vacuum.py b/custom_components/purei9/vacuum.py index b8e31ce..283c848 100644 --- a/custom_components/purei9/vacuum.py +++ b/custom_components/purei9/vacuum.py @@ -80,8 +80,8 @@ def battery_level(self) -> int: return self._params.battery @property - def state(self) -> str: - """Check Home Assistant state variables""" + def activity(self) -> VacuumActivity: + """Return the current vacuum activity""" return self._params.state @property From c9e91099d78322207f4119c77ea792fbe2ca3d95 Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:42:00 +0100 Subject: [PATCH 3/6] Bump python version to 3.13 in circleci --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6d7f35f..821e2ab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ commands: jobs: lint: docker: - - image: cimg/python:3.11 + - image: cimg/python:3.13 resource_class: small steps: - checkout @@ -27,7 +27,7 @@ jobs: - run: find . -name "*.json" -type f -print0 | xargs -0I {} python3 -m json.tool {} test: docker: - - image: cimg/python:3.11 + - image: cimg/python:3.13 resource_class: small steps: - checkout From 9a6aabe91a8019abe3bab4d21850f7d0acb809b6 Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:47:14 +0100 Subject: [PATCH 4/6] Change tests to match VacuumState instead and lint imports --- custom_components/purei9/purei9.py | 4 +--- tests/test_purei9.py | 12 ++++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/custom_components/purei9/purei9.py b/custom_components/purei9/purei9.py index 4408e90..4b6ae57 100644 --- a/custom_components/purei9/purei9.py +++ b/custom_components/purei9/purei9.py @@ -9,9 +9,7 @@ CleaningSession, ) from purei9_unofficial.cloudv3 import CloudZone, CloudMap -from homeassistant.components.vacuum import ( - VacuumActivity -) +from homeassistant.components.vacuum import VacuumActivity from . import const # See: https://github.com/Phype/purei9_unofficial/blob/master/src/purei9_unofficial/common.py diff --git a/tests/test_purei9.py b/tests/test_purei9.py index 69393c8..709199c 100644 --- a/tests/test_purei9.py +++ b/tests/test_purei9.py @@ -1,20 +1,16 @@ """Test the purei9 module""" import unittest from purei9_unofficial.common import BatteryStatus, RobotStates, PowerMode, DustbinStates -from homeassistant.components.vacuum import ( - STATE_CLEANING, - STATE_DOCKED, - STATE_IDLE -) +from homeassistant.components.vacuum import VacuumActivity from custom_components.purei9 import purei9 class TestPureI9(unittest.TestCase): """Tests for the purei9 module""" data_state_to_hass = [ # No need to test every single case. The test will become too fragile. - (RobotStates.Cleaning, BatteryStatus.Dead, STATE_CLEANING), - (RobotStates.Sleeping, BatteryStatus.High, STATE_DOCKED), - (RobotStates.Sleeping, BatteryStatus.Normal, STATE_IDLE), + (RobotStates.Cleaning, BatteryStatus.Dead, VacuumActivity.CLEANING), + (RobotStates.Sleeping, BatteryStatus.High, VacuumActivity.DOCKED), + (RobotStates.Sleeping, BatteryStatus.Normal, VacuumActivity.IDLE), ] def test_state_to_hass(self): From 7ac67f9e551dce424893626ffebc106ee90a6f36 Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:54:10 +0100 Subject: [PATCH 5/6] Lint too long line --- custom_components/purei9/purei9.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/custom_components/purei9/purei9.py b/custom_components/purei9/purei9.py index 4b6ae57..490044b 100644 --- a/custom_components/purei9/purei9.py +++ b/custom_components/purei9/purei9.py @@ -39,7 +39,8 @@ def state_to_hass( # In order to detect if it's docket or if it's just idling in the middle of a room # check the battery level. If it's full then we're docked. if pure_i9_state == RobotStates.Sleeping: - return VacuumActivity.DOCKED if pure_i9_battery == BatteryStatus.High else VacuumActivity.IDLE + return (VacuumActivity.DOCKED if pure_i9_battery == BatteryStatus.High + else VacuumActivity.IDLE) return PURE_I9_STATE_MAP.get(pure_i9_state, VacuumActivity.IDLE) From ebcd1115dd1dc22dfbdebbd64a6df0b2159f2dfc Mon Sep 17 00:00:00 2001 From: Oskar Stenberg <01ste02@gmail.com> Date: Sat, 11 Jan 2025 20:55:49 +0100 Subject: [PATCH 6/6] Update Home Assistant version and Python version in docker-compose as well --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1754f80..d0c990a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3" services: # localhost:8123 hass: - image: homeassistant/home-assistant:2024.1.0 + image: homeassistant/home-assistant:2025.1.2 restart: unless-stopped volumes: - ./hass:/config @@ -11,7 +11,7 @@ services: TZ: Europe/Stockholm ports: [ "8123:8123" ] lint: - image: cimg/python:3.11 + image: cimg/python:3.13 volumes: [ "./:/files:ro" ] working_dir: /files entrypoint: [ pylint ] @@ -19,7 +19,7 @@ services: - custom_components/purei9 - tests test: - image: cimg/python:3.11 + image: cimg/python:3.13 volumes: [ "./:/files:ro" ] working_dir: /files entrypoint: [ python3 ]