Deebot-4-Home-Assistant icon indicating copy to clipboard operation
Deebot-4-Home-Assistant copied to clipboard

Add getPosition event with x y and currentSpotAreaID

Open francescopalma86 opened this issue 1 year ago • 3 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues and no issue is describing my feature request or idea

Is your feature request related to a problem?

No response

Suggested solution

My goal is to turn on the light in the room where the robot is cleaning and turn it off when it exits the room. I have studied code and examples online, and I have found that the ecovacs-deebot.js library provides the following information: along with the x and y coordinates, it also has the currentSpotAreaID property.

In vacBot.js -> let currentSpotAreaID = mapTools.isPositionInSpotArea(posX, posY, this.mapSpotAreaInfos[this.currentMapMID]);

After retrieving the polygon of each room and the current position of the robot, the process to calculate the area it is in should be as follows:

  1. Decompress the coordinates with lzma and decode the base64: polygon_string = _decompress_7z_base64_data(event.coordinates).decode('utf-8');

  2. Use a function like the following for each room to determine in which polygon the current bot coordinates are located:

import shapely.geometry

def is_point_inside_polygon(x, y, polygon_string):
    coordinates = [tuple(map(float, pair.split(','))) for pair in polygon_string.split(';')]
    polygon = shapely.geometry.Polygon(coordinates)
    return polygon.contains(shapely.geometry.Point(x, y))
  1. Now, you could trigger a new event or modify the value of a new sensor to inform the user that the bot has changed rooms.

Api example request

No response

Alternatives you've considered

No response

Additional information

No response

francescopalma86 avatar Jan 14 '24 11:01 francescopalma86

I wrote this example

import aiohttp
import asyncio
import logging
import time
import shapely.geometry

from deebot_client.api_client import ApiClient
from deebot_client.authentication import Authenticator
from deebot_client.commands.json import GetMapSubSet, GetMapSet, GetPos
from deebot_client.events import MapSubsetEvent, MapSetEvent, PositionsEvent
from deebot_client.map import _decompress_7z_base64_data
from deebot_client.models import Configuration
from deebot_client.mqtt_client import MqttClient, MqttConfiguration
from deebot_client.util import md5
from deebot_client.device import Device

device_id = md5(str(time.time()))
account_id = ""
password_hash = md5("")
country = "IT"
continent = "eu"
mapId = ""

lastDeebotArea = None


async def main():
    async with aiohttp.ClientSession() as session:
        logging.basicConfig(level=logging.DEBUG)
        config = Configuration(session, device_id=device_id, country=country, continent=continent)

        authenticator = Authenticator(config, account_id, password_hash)
        api_client = ApiClient(authenticator)

        devices_ = await api_client.get_devices()

        bot = Device(devices_[0], authenticator)

        mqtt_config = MqttConfiguration(config=config)
        mqtt = MqttClient(mqtt_config, authenticator)
        await bot.initialize(mqtt)

        subsets = []

        def get_room_dict_from(event: MapSubsetEvent):
            s_coords = _decompress_7z_base64_data(event.coordinates).decode('utf-8')
            coordinates = [tuple(map(float, pair.split(','))) for pair in s_coords.split(';')]
            polygon = shapely.geometry.Polygon(coordinates)
            return {
                "id": event.id,
                "name": event.name,
                "area": polygon
            }

        async def on_mapsubset(event: MapSubsetEvent):
            if event.type == "ar":
                subsets.append(get_room_dict_from(event))

        async def on_mapset(event: MapSetEvent):
            if event.type == "ar":
                subsets.clear()
                for room_id in event.subsets:
                    await bot.execute_command(GetMapSubSet(mid=mapId, mssid=str(room_id), type="ar", msid="0"))

        async def on_position(event: PositionsEvent):
            deebot_pos = event.positions[0]
            deebot_point = shapely.geometry.Point(deebot_pos.x, deebot_pos.y)
            print(deebot_pos.x, deebot_pos.y)
            global lastDeebotArea
            for subset in subsets:
                if subset["area"].contains(deebot_point):
                    if subset['name'] != lastDeebotArea:
                        lastDeebotArea = subset["name"]
                        print(f"Deebot has changed area: {subset['name']}")
                    break

        bot.events.subscribe(PositionsEvent, on_position)
        bot.events.subscribe(MapSetEvent, on_mapset)
        bot.events.subscribe(MapSubsetEvent, on_mapsubset)

        # Execute commands
        await bot.execute_command(GetMapSet(mid=mapId))

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.create_task(main())
    loop.run_forever()

in this example print new Area when deebot change her, now i miss homassistant dev skill

francescopalma86 avatar Jan 14 '24 16:01 francescopalma86

Thanks for your improvement. Could you open a PR directly in https://github.com/DeebotUniverse/client.py? Afterwards I can add it to the component

edenhaus avatar Jan 15 '24 08:01 edenhaus

I released implementation in currentSpotAreaID brench and opened a new pull request. After I forked Deebot-4-Home-Assistant and homeassistant core.

Now I don't know how to proceed.

I also finished the total implementation and this is the result Screenshot 2024-01-18 alle 20 34 50

but I did everything manually, because I don't know the process of implementing custom integrations and development on home assistants in general

francescopalma86 avatar Jan 18 '24 19:01 francescopalma86