Skip to content

Commit db39b31

Browse files
committed
init commit
0 parents  commit db39b31

9 files changed

Lines changed: 245 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.idea

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Roborock Custom Map
2+
3+
This allows you to use the core Roborock integration with the [Xiaomi Map Card](https://github.com/PiotrMachowski/lovelace-xiaomi-vacuum-map-card)
4+
5+
If you would like to support me, you can do so here:
6+
7+
[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]
8+
9+
[![PaypalMe][paypalmebadge]][paypalme]
10+
11+
### Setup
12+
13+
1. Install the [Roborock Core Integration](https://my.home-assistant.io/redirect/config_flow_start?domain=roborock) and set it up
14+
2. It is recommended that you first disable the Image entities within the core integration. Open each image entity, hit the gear icon, then trigger the toggle by enabled.
15+
3. Install this integration
16+
4. This integration works by piggybacking off of the Core integration, so the Core integration will do all the data updating to help prevent rate-limits. But that means that the core integration must be setup and loaded first. If you run into any issues, make sure the Roborock integration is loaded first, and then reload this one.
17+
5. Setup the map card like normal! An example configuration would look like
18+
```yaml
19+
type: custom:xiaomi-vacuum-map-card
20+
vacuum_platform: roborock
21+
entity: vacuum.s7
22+
map_source:
23+
camera: image.s7_downstairs_full_custom
24+
calibration_source:
25+
camera: true
26+
```
27+
6. You can hit Generate Room Configs to allow for cleaning of rooms. It might generate extra keys, so check the yaml and make sure there are no extra 'predefined_sections'
28+
29+
30+
### Installation
31+
32+
### Installing via HACS
33+
[![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=Lash-L&repository=RoborockCustomMap&category=integration)
34+
35+
or
36+
37+
1. Go to HACS->Integrations
38+
1. Add this repo(https://github.com/Lash-L/RoborockCustomMap) into your HACS custom repositories
39+
1. Search for Roborock Custom Map and Download it
40+
1. Restart your HomeAssistant
41+
1. Go to Settings->Devices & Services
42+
1. Add the Roborock Custom Map integration
43+
44+
45+
46+
[buymecoffee]: https://www.buymeacoffee.com/LashL
47+
[buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge
48+
[paypalme]: https://paypal.me/LLashley304
49+
[paypalmebadge]: https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg
50+
[hacsbutton]: https://my.home-assistant.io/redirect/hacs_repository/?owner=Lash-L&repository=tempofit&category=integration
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
"""Roborock Custom Map integration."""
2+
3+
from __future__ import annotations
4+
5+
from homeassistant.config_entries import ConfigEntry
6+
from homeassistant.const import Platform
7+
from homeassistant.core import HomeAssistant
8+
from homeassistant.config_entries import ConfigEntryState
9+
from homeassistant.exceptions import ConfigEntryNotReady
10+
11+
PLATFORMS = [Platform.IMAGE]
12+
13+
14+
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
15+
"""Set up Roborock Custom map from a config entry."""
16+
roborock_entries = hass.config_entries.async_entries("roborock")
17+
coordinators = []
18+
19+
async def unload_this_entry():
20+
await hass.config_entries.async_reload(entry.entry_id)
21+
22+
for r_entry in roborock_entries:
23+
if r_entry.state == ConfigEntryState.LOADED:
24+
coordinators.extend(r_entry.runtime_data.v1)
25+
# If any unload, then we should reload as well in case there are major changes.
26+
r_entry.async_on_unload(unload_this_entry)
27+
if len(coordinators) == 0:
28+
raise ConfigEntryNotReady("No Roborock entries loaded. Cannot start.")
29+
entry.runtime_data = coordinators
30+
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
31+
32+
return True
33+
34+
35+
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
36+
"""Unload a config entry."""
37+
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""Config flow for Roborock Custom Map integration."""
2+
3+
from __future__ import annotations
4+
5+
from typing import Any
6+
7+
from homeassistant import config_entries
8+
from homeassistant.data_entry_flow import FlowResult
9+
10+
from .const import DOMAIN
11+
12+
13+
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
14+
"""Handle a config flow for Roborock Custom Map."""
15+
16+
VERSION = 1
17+
18+
async def async_step_user(
19+
self, user_input: dict[str, Any] | None = None
20+
) -> FlowResult:
21+
"""Handle the initial step."""
22+
self.async_set_unique_id(DOMAIN)
23+
self._abort_if_unique_id_configured()
24+
return self.async_create_entry(title="Roborock Custom Map", data={})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Constants for Roborock Custom Map integration."""
2+
3+
DOMAIN = "roborock_custom_map"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""Support for Roborock image."""
2+
3+
from datetime import datetime
4+
import logging
5+
6+
from homeassistant.components.image import ImageEntity
7+
from homeassistant.components.roborock.coordinator import RoborockDataUpdateCoordinator
8+
from homeassistant.components.roborock.entity import RoborockCoordinatedEntityV1
9+
from homeassistant.config_entries import ConfigEntry
10+
from homeassistant.const import EntityCategory
11+
from homeassistant.core import HomeAssistant
12+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
13+
14+
_LOGGER = logging.getLogger(__name__)
15+
16+
PARALLEL_UPDATES = 0
17+
18+
19+
async def async_setup_entry(
20+
hass: HomeAssistant,
21+
config_entry,
22+
async_add_entities: AddConfigEntryEntitiesCallback,
23+
) -> None:
24+
"""Set up Roborock image platform."""
25+
26+
async_add_entities(
27+
(
28+
RoborockMap(
29+
config_entry,
30+
f"{coord.duid_slug}_custom_map_{map_info.name}",
31+
coord,
32+
map_info.flag,
33+
map_info.name,
34+
)
35+
for coord in config_entry.runtime_data
36+
for map_info in coord.maps.values()
37+
),
38+
)
39+
40+
41+
class RoborockMap(RoborockCoordinatedEntityV1, ImageEntity):
42+
"""A class to let you visualize the map."""
43+
44+
_attr_has_entity_name = True
45+
image_last_updated: datetime
46+
_attr_name: str
47+
48+
def __init__(
49+
self,
50+
config_entry: ConfigEntry,
51+
unique_id: str,
52+
coordinator: RoborockDataUpdateCoordinator,
53+
map_flag: int,
54+
map_name: str,
55+
) -> None:
56+
"""Initialize a Roborock map."""
57+
RoborockCoordinatedEntityV1.__init__(self, unique_id, coordinator)
58+
ImageEntity.__init__(self, coordinator.hass)
59+
self.config_entry = config_entry
60+
self._attr_name = map_name + "_custom"
61+
self.map_flag = map_flag
62+
self.cached_map = b""
63+
self._attr_entity_category = EntityCategory.DIAGNOSTIC
64+
65+
@property
66+
def is_selected(self) -> bool:
67+
"""Return if this map is the currently selected map."""
68+
return self.map_flag == self.coordinator.current_map
69+
70+
async def async_added_to_hass(self) -> None:
71+
"""When entity is added to hass load any previously cached maps from disk."""
72+
await super().async_added_to_hass()
73+
self._attr_image_last_updated = self.coordinator.maps[
74+
self.map_flag
75+
].last_updated
76+
self.async_write_ha_state()
77+
78+
def _handle_coordinator_update(self) -> None:
79+
# If the coordinator has updated the map, we can update the image.
80+
self._attr_image_last_updated = self.coordinator.maps[
81+
self.map_flag
82+
].last_updated
83+
84+
super()._handle_coordinator_update()
85+
86+
async def async_image(self) -> bytes | None:
87+
"""Get the cached image."""
88+
return self.coordinator.maps[self.map_flag].image
89+
90+
@property
91+
def extra_state_attributes(self):
92+
map_data = self.coordinator.maps[self.map_flag].map_data
93+
if map_data is None:
94+
return {}
95+
for room in map_data.rooms.values():
96+
room.name = self.coordinator.maps[self.map_flag].rooms.get(room.number)
97+
98+
return {
99+
"calibration_points": self.coordinator.maps[
100+
self.map_flag
101+
].map_data.calibration(),
102+
"rooms": map_data.rooms,
103+
"zones": map_data.zones,
104+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"domain": "roborock_custom_map",
3+
"name": "Roborock Custom Map",
4+
"codeowners": ["@Lash-L"],
5+
"config_flow": true,
6+
"dependencies": ["roborock"],
7+
"documentation": "https://github.com/Lash-L/RoborockCustomMap",
8+
"iot_class": "local_polling",
9+
"issue_tracker": "https://github.com/Lash-L/RoborockCustomMap/issues",
10+
"requirements": [],
11+
12+
"version": "0.1.0"
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"config": {
3+
"abort": {
4+
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
5+
}
6+
}
7+
}

hacs.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Roborock Custom Map",
3+
"hacs": "0.1.0",
4+
"homeassistant": "2025.4",
5+
"domains": ["image"]
6+
}

0 commit comments

Comments
 (0)