init commit

This commit is contained in:
Luke
2025-03-22 11:15:33 -04:00
commit db39b317e9
9 changed files with 245 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
.idea
+50
View File
@@ -0,0 +1,50 @@
# Roborock Custom Map
This allows you to use the core Roborock integration with the [Xiaomi Map Card](https://github.com/PiotrMachowski/lovelace-xiaomi-vacuum-map-card)
If you would like to support me, you can do so here:
[![BuyMeCoffee][buymecoffeebadge]][buymecoffee]
[![PaypalMe][paypalmebadge]][paypalme]
### Setup
1. Install the [Roborock Core Integration](https://my.home-assistant.io/redirect/config_flow_start?domain=roborock) and set it up
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.
3. Install this integration
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.
5. Setup the map card like normal! An example configuration would look like
```yaml
type: custom:xiaomi-vacuum-map-card
vacuum_platform: roborock
entity: vacuum.s7
map_source:
camera: image.s7_downstairs_full_custom
calibration_source:
camera: true
```
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'
### Installation
### Installing via HACS
[![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)
or
1. Go to HACS->Integrations
1. Add this repo(https://github.com/Lash-L/RoborockCustomMap) into your HACS custom repositories
1. Search for Roborock Custom Map and Download it
1. Restart your HomeAssistant
1. Go to Settings->Devices & Services
1. Add the Roborock Custom Map integration
[buymecoffee]: https://www.buymeacoffee.com/LashL
[buymecoffeebadge]: https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg?style=for-the-badge
[paypalme]: https://paypal.me/LLashley304
[paypalmebadge]: https://cdn.rawgit.com/twolfson/paypal-github-button/1.0.0/dist/button.svg
[hacsbutton]: https://my.home-assistant.io/redirect/hacs_repository/?owner=Lash-L&repository=tempofit&category=integration
@@ -0,0 +1,37 @@
"""Roborock Custom Map integration."""
from __future__ import annotations
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.config_entries import ConfigEntryState
from homeassistant.exceptions import ConfigEntryNotReady
PLATFORMS = [Platform.IMAGE]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Roborock Custom map from a config entry."""
roborock_entries = hass.config_entries.async_entries("roborock")
coordinators = []
async def unload_this_entry():
await hass.config_entries.async_reload(entry.entry_id)
for r_entry in roborock_entries:
if r_entry.state == ConfigEntryState.LOADED:
coordinators.extend(r_entry.runtime_data.v1)
# If any unload, then we should reload as well in case there are major changes.
r_entry.async_on_unload(unload_this_entry)
if len(coordinators) == 0:
raise ConfigEntryNotReady("No Roborock entries loaded. Cannot start.")
entry.runtime_data = coordinators
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
@@ -0,0 +1,24 @@
"""Config flow for Roborock Custom Map integration."""
from __future__ import annotations
from typing import Any
from homeassistant import config_entries
from homeassistant.data_entry_flow import FlowResult
from .const import DOMAIN
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Roborock Custom Map."""
VERSION = 1
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
self.async_set_unique_id(DOMAIN)
self._abort_if_unique_id_configured()
return self.async_create_entry(title="Roborock Custom Map", data={})
@@ -0,0 +1,3 @@
"""Constants for Roborock Custom Map integration."""
DOMAIN = "roborock_custom_map"
@@ -0,0 +1,104 @@
"""Support for Roborock image."""
from datetime import datetime
import logging
from homeassistant.components.image import ImageEntity
from homeassistant.components.roborock.coordinator import RoborockDataUpdateCoordinator
from homeassistant.components.roborock.entity import RoborockCoordinatedEntityV1
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
config_entry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Roborock image platform."""
async_add_entities(
(
RoborockMap(
config_entry,
f"{coord.duid_slug}_custom_map_{map_info.name}",
coord,
map_info.flag,
map_info.name,
)
for coord in config_entry.runtime_data
for map_info in coord.maps.values()
),
)
class RoborockMap(RoborockCoordinatedEntityV1, ImageEntity):
"""A class to let you visualize the map."""
_attr_has_entity_name = True
image_last_updated: datetime
_attr_name: str
def __init__(
self,
config_entry: ConfigEntry,
unique_id: str,
coordinator: RoborockDataUpdateCoordinator,
map_flag: int,
map_name: str,
) -> None:
"""Initialize a Roborock map."""
RoborockCoordinatedEntityV1.__init__(self, unique_id, coordinator)
ImageEntity.__init__(self, coordinator.hass)
self.config_entry = config_entry
self._attr_name = map_name + "_custom"
self.map_flag = map_flag
self.cached_map = b""
self._attr_entity_category = EntityCategory.DIAGNOSTIC
@property
def is_selected(self) -> bool:
"""Return if this map is the currently selected map."""
return self.map_flag == self.coordinator.current_map
async def async_added_to_hass(self) -> None:
"""When entity is added to hass load any previously cached maps from disk."""
await super().async_added_to_hass()
self._attr_image_last_updated = self.coordinator.maps[
self.map_flag
].last_updated
self.async_write_ha_state()
def _handle_coordinator_update(self) -> None:
# If the coordinator has updated the map, we can update the image.
self._attr_image_last_updated = self.coordinator.maps[
self.map_flag
].last_updated
super()._handle_coordinator_update()
async def async_image(self) -> bytes | None:
"""Get the cached image."""
return self.coordinator.maps[self.map_flag].image
@property
def extra_state_attributes(self):
map_data = self.coordinator.maps[self.map_flag].map_data
if map_data is None:
return {}
for room in map_data.rooms.values():
room.name = self.coordinator.maps[self.map_flag].rooms.get(room.number)
return {
"calibration_points": self.coordinator.maps[
self.map_flag
].map_data.calibration(),
"rooms": map_data.rooms,
"zones": map_data.zones,
}
@@ -0,0 +1,13 @@
{
"domain": "roborock_custom_map",
"name": "Roborock Custom Map",
"codeowners": ["@Lash-L"],
"config_flow": true,
"dependencies": ["roborock"],
"documentation": "https://github.com/Lash-L/RoborockCustomMap",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/Lash-L/RoborockCustomMap/issues",
"requirements": [],
"version": "0.1.0"
}
@@ -0,0 +1,7 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
}
}
}
+6
View File
@@ -0,0 +1,6 @@
{
"name": "Roborock Custom Map",
"hacs": "0.1.0",
"homeassistant": "2025.4",
"domains": ["image"]
}