Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to follow Vacuum structure from Home Assistant 2025.1.0 #102

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ commands:
jobs:
lint:
docker:
- image: cimg/python:3.11
- image: cimg/python:3.13
resource_class: small
steps:
- checkout
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion custom_components/purei9/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
40 changes: 17 additions & 23 deletions custom_components/purei9/purei9.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,25 @@
CleaningSession,
)
from purei9_unofficial.cloudv3 import CloudZone, CloudMap
from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_DOCKED,
STATE_ERROR,
STATE_IDLE,
STATE_PAUSED,
STATE_RETURNING
)
from homeassistant.components.vacuum import 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(
Expand All @@ -46,9 +39,10 @@ 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 = {
Expand Down Expand Up @@ -106,7 +100,7 @@ def params_map_create(cloud_map: CloudMap) -> ParamsMap:
class Params:
"""Data available in the state"""
battery: int = 100
state: str = STATE_IDLE
state: VacuumActivity = VacuumActivity.IDLE
available: bool = True
firmware: str = None
fan_speed: str = POWER_MODE_POWER
Expand Down
23 changes: 10 additions & 13 deletions custom_components/purei9/vacuum.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,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
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -11,15 +11,15 @@ 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 ]
command:
- custom_components/purei9
- tests
test:
image: cimg/python:3.11
image: cimg/python:3.13
volumes: [ "./:/files:ro" ]
working_dir: /files
entrypoint: [ python3 ]
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
purei9_unofficial==0.0.16
homeassistant==2024.1.0
homeassistant==2025.1.0
pylint==3.3.1
12 changes: 4 additions & 8 deletions tests/test_purei9.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down