-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
Add Roomba J9 compatibility #145913
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
Draft
rokam
wants to merge
5
commits into
home-assistant:dev
Choose a base branch
from
rokam:feat_roomba_j7
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+366
−1
Draft
Add Roomba J9 compatibility #145913
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
9ff46a4
feat(j9): add roomba j9 information
rokam 52f9f73
Merge branch 'home-assistant:dev' into feat_roomba_j7
rokam 2e7d6c0
test(roomba): add coverage
rokam d94f882
Merge branch 'dev' into feat_roomba_j7
rokam 437d858
test(roomba): more coverage
rokam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,16 @@ class RoombaSensorEntityDescription(SensorEntityDescription): | |
value_fn: Callable[[IRobotEntity], StateType] | ||
|
||
|
||
DOCK_SENSORS: list[RoombaSensorEntityDescription] = [ | ||
RoombaSensorEntityDescription( | ||
key="dock_tank_level", | ||
translation_key="dock_tank_level", | ||
native_unit_of_measurement=PERCENTAGE, | ||
entity_category=EntityCategory.DIAGNOSTIC, | ||
value_fn=lambda self: self.dock_tank_level, | ||
), | ||
] | ||
|
||
SENSORS: list[RoombaSensorEntityDescription] = [ | ||
RoombaSensorEntityDescription( | ||
key="battery", | ||
|
@@ -37,6 +47,13 @@ class RoombaSensorEntityDescription(SensorEntityDescription): | |
entity_category=EntityCategory.DIAGNOSTIC, | ||
value_fn=lambda self: self.battery_level, | ||
), | ||
RoombaSensorEntityDescription( | ||
key="tank_level", | ||
translation_key="tank_level", | ||
native_unit_of_measurement=PERCENTAGE, | ||
entity_category=EntityCategory.DIAGNOSTIC, | ||
value_fn=lambda self: self.tank_level, | ||
), | ||
RoombaSensorEntityDescription( | ||
key="battery_cycles", | ||
translation_key="battery_cycles", | ||
|
@@ -136,6 +153,15 @@ async def async_setup_entry( | |
RoombaSensor(roomba, blid, entity_description) for entity_description in SENSORS | ||
) | ||
|
||
dock_entities: list[RoombaSensor] = [] | ||
for entity_description in DOCK_SENSORS: | ||
entity = RoombaSensor(roomba, blid, entity_description) | ||
if entity.has_dock: | ||
dock_entities.append(entity) | ||
|
||
if len(dock_entities) > 0: | ||
async_add_entities(dock_entities) | ||
Comment on lines
153
to
+163
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should call |
||
|
||
|
||
class RoombaSensor(IRobotEntity, SensorEntity): | ||
"""Roomba sensor.""" | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
"""Tests for iRobotEntity properties in the Roomba integration.""" | ||
|
||
from typing import Any | ||
from unittest.mock import MagicMock | ||
|
||
import pytest | ||
from roombapy import Roomba | ||
|
||
from homeassistant.components.roomba.entity import IRobotEntity | ||
|
||
|
||
@pytest.fixture | ||
def mock_roomba_state() -> dict[str, Any]: | ||
"""Fixture to provide a mock Roomba state.""" | ||
return { | ||
"tankLvl": 42, | ||
"dock": {"tankLvl": 99}, | ||
"hwPartsRev": {"navSerialNo": "12345", "wlan0HwAddr": "AA:BB:CC:DD:EE:FF"}, | ||
"sku": "980", | ||
"name": "Test Roomba", | ||
"softwareVer": "3.2.1", | ||
"hardwareRev": "1.0", | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def mock_roomba(mock_roomba_state: dict[str, Any]) -> Roomba: | ||
"""Fixture to create a mock Roomba instance.""" | ||
roomba = MagicMock() | ||
roomba.master_state = {"state": {"reported": mock_roomba_state}} | ||
return roomba | ||
|
||
|
||
class DummyEntity(IRobotEntity): | ||
"""Dummy Roomba entity for testing purposes.""" | ||
|
||
def on_message(self, json_data: dict[str, Any]) -> None: | ||
"""Handle incoming messages.""" | ||
|
||
|
||
def test_tank_level_property(mock_roomba: Roomba) -> None: | ||
"""Test the tank level property of the IRobotEntity.""" | ||
entity = DummyEntity(mock_roomba, "blid123") | ||
assert entity.tank_level == 42 | ||
|
||
|
||
def test_has_dock_property(mock_roomba: Roomba) -> None: | ||
"""Test the has_dock property of the IRobotEntity.""" | ||
entity = DummyEntity(mock_roomba, "blid123") | ||
assert entity.has_dock is True | ||
|
||
|
||
def test_dock_tank_level_property(mock_roomba: Roomba) -> None: | ||
"""Test the dock tank level property of the IRobotEntity.""" | ||
entity = DummyEntity(mock_roomba, "blid123") | ||
assert entity.dock_tank_level == 99 | ||
|
||
|
||
def test_has_dock_property_false() -> None: | ||
"""Test has_dock property returns False when dock is empty.""" | ||
mock_state = { | ||
"tankLvl": 42, | ||
"dock": {}, | ||
"hwPartsRev": {"navSerialNo": "12345", "wlan0HwAddr": "AA:BB:CC:DD:EE:FF"}, | ||
"sku": "980", | ||
"name": "Test Roomba", | ||
"softwareVer": "3.2.1", | ||
"hardwareRev": "1.0", | ||
} | ||
roomba = MagicMock() | ||
roomba.master_state = {"state": {"reported": mock_state}} | ||
entity = DummyEntity(roomba, "blid123") | ||
assert entity.has_dock is False | ||
|
||
|
||
def test_tank_level_none() -> None: | ||
"""Test tank_level property returns None if not present.""" | ||
mock_state = { | ||
"dock": {"tankLvl": 99}, | ||
"hwPartsRev": {"navSerialNo": "12345", "wlan0HwAddr": "AA:BB:CC:DD:EE:FF"}, | ||
"sku": "980", | ||
"name": "Test Roomba", | ||
"softwareVer": "3.2.1", | ||
"hardwareRev": "1.0", | ||
} | ||
roomba = MagicMock() | ||
roomba.master_state = {"state": {"reported": mock_state}} | ||
entity = DummyEntity(roomba, "blid123") | ||
assert entity.tank_level is None | ||
|
||
|
||
def test_dock_tank_level_none() -> None: | ||
"""Test dock_tank_level property returns None if not present.""" | ||
mock_state = { | ||
"tankLvl": 42, | ||
"dock": {}, | ||
"hwPartsRev": {"navSerialNo": "12345", "wlan0HwAddr": "AA:BB:CC:DD:EE:FF"}, | ||
"sku": "980", | ||
"name": "Test Roomba", | ||
"softwareVer": "3.2.1", | ||
"hardwareRev": "1.0", | ||
} | ||
roomba = MagicMock() | ||
roomba.master_state = {"state": {"reported": mock_state}} | ||
entity = DummyEntity(roomba, "blid123") | ||
assert entity.dock_tank_level is None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"""Tests for IRobotEntity usage in Roomba sensor platform.""" | ||
|
||
from collections.abc import Iterable | ||
from typing import Any | ||
from unittest.mock import MagicMock | ||
|
||
import pytest | ||
from roombapy import Roomba | ||
|
||
from homeassistant.components.roomba.models import RoombaData | ||
from homeassistant.components.roomba.sensor import async_setup_entry | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.entity import Entity | ||
|
||
|
||
@pytest.fixture | ||
def mock_roomba_state() -> dict[str, Any]: | ||
"""Fixture to provide a mock Roomba state.""" | ||
return { | ||
"cap": {"pose": 1}, | ||
"cleanMissionStatus": {"cycle": "none", "phase": "charge"}, | ||
"softwareVer": "3.2.1", | ||
"pose": {"point": {"x": 1, "y": 2}, "theta": 90}, | ||
"hwPartsRev": {"navSerialNo": "12345", "wlan0HwAddr": "AA:BB:CC:DD:EE:FF"}, | ||
"sku": "980", | ||
"name": "Test Roomba", | ||
"hardwareRev": "1.0", | ||
"bin": {"present": True, "full": False}, | ||
} | ||
|
||
|
||
@pytest.fixture | ||
def mock_roomba(mock_roomba_state: dict[str, Any]) -> Roomba: | ||
"""Fixture to create a mock Roomba vacuum instance.""" | ||
roomba = MagicMock() | ||
roomba.send_command = MagicMock() | ||
roomba.error_code = 0 | ||
roomba.error_message = None | ||
roomba.current_state = "run" | ||
roomba.set_preference = MagicMock() | ||
roomba.register_on_message_callback = MagicMock() | ||
roomba.master_state = {"state": {"reported": mock_roomba_state}} | ||
return roomba | ||
|
||
|
||
@pytest.mark.asyncio | ||
@pytest.mark.parametrize( | ||
("state", "expected_sensors"), | ||
[ | ||
( | ||
{}, | ||
12, | ||
), | ||
({"dock": {}}, 12), | ||
({"dock": {"tankLvl": 10}}, 13), | ||
], | ||
) | ||
async def test_async_setup_entry_selects_correct_class( | ||
mock_roomba: Roomba, | ||
state: dict[str, Any], | ||
expected_sensors: int, | ||
) -> None: | ||
"""Test async_setup_entry selects the correct amount of sensors based on state.""" | ||
# Setup mocks | ||
hass = MagicMock(spec=HomeAssistant) | ||
config_entry = MagicMock(spec=ConfigEntry) | ||
config_entry.entry_id = "test_entry" | ||
master_state = {"state": {"reported": state}} | ||
mock_roomba.master_state.update(master_state) | ||
blid = "blid123" | ||
hass.data = {"roomba": {"test_entry": RoombaData(roomba=mock_roomba, blid=blid)}} | ||
|
||
added_entities: list[Entity] = [] | ||
|
||
def async_add_entities( | ||
new_entities: Iterable[Entity], | ||
update_before_add: bool = False, | ||
*, | ||
config_subentry_id: str | None = None, | ||
) -> None: | ||
added_entities.extend(list(new_entities)) | ||
|
||
await async_setup_entry(hass, config_entry, async_add_entities) | ||
assert len(added_entities) == expected_sensors |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should move the
has_dock
out of the entity, because it will be the same for every entity, and we could move that if statement to before the for loopThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I agree, but can you point me in the right direction to do it? I don't know where to put that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have access to the vacuum object at this point, so I would assume that we can just read the state from there