Sdahl1234 / Sunseeker-lawn-mower

10 stars 4 forks source link

Missing Map #4

Open povlhp opened 4 months ago

povlhp commented 4 months ago

I have a Grouw Clevr 1000. It has a map display after first edge cut. Showing length of perimeter and area as well as the map itself.

What can I do to help get it into HA ?

Is it just setting the phone to use a Burp or Fiddler proxy to log traffic ? Or is there any additional encryption layer, certificate pinning etc on top ? Or was the integration done by decompiling the Android App ?

Sdahl1234 commented 4 months ago

Try enable debug log in the integration. This will log all data received.

povlhp commented 4 months ago

There are some sensors missing for me:

All mull_meter are distance clockwise along perimeter in cm to the zone entry point. Can later be used with map.

I think they overrule the mul_zonX values. mul_proX is the percentage of time this zone is supposed to be used.

2024-04-21 07:41:38.272 DEBUG (Thread-3 (_thread. _main)) [cust om_components.sunseeker.sunseeker] MQTT message: /app/xxx/get {"mul_meter1": 6912, "mul_meter2": 34562, "mul_meter3": 4424 0, "mul_meter4": 103687, "mul_en": true, "mul. _auto": false, "mul. _zon 1": 5, "mul_mode": 0, "mul_zon2":25, "mul_pro1":50, "mul_zon3": 32," deviceSn":"xxx" , "mul_zon4": 75, "mul_pro4": 20, "mul_pro3":0, "mul_pro2": 30, "cmd": 507}

povlhp commented 4 months ago

More unparsed data on mine. On_min/total_min is the same on mine. It is total time in minutes. One of them should be exposed.

Cur_area seems to be increasing when mode = 1. Not sure about units. Could be 10* m2 mowed. Is 42 after 1 min - 210 after 5 min.

With 1.2km/h = 0.33m/s we have max 18m/min. Multiply by 0.2m cutting diameter that is theoretical max of 3.2m2/min. My guess is that we are closer to 0.42m2 when it says 42.

After 60 minutes it says 2520. Thus likely 25.2m2.

After mowing 228 minutes it says 9492. Almost 4hours and less than 100m2 ?? I think the number might be too low. Say average is 3m before hitting something, that is 10s, then 5s to stop/turn be up to speed. It should have a cutting speed of 2/3 or at least half of max.

Guess I have to do something manual. Say measure 10 or 20 meters. Let it cut/move straight and see result.

2024-04-21 10:52:14.388 DEBUG (Thread-3 (_thread _main)) [cust om_components.sunseeker.sunseeker] MQTT message: /app/xxx1/get {"mode": 1, "cur_min":60, "cur _area":2520, "wifi_lv":3, "on min": 1235, "station"; false, "cmd":501, "power": 74, "total_min": 1235, "on_area":889, "deviceSn":"xxx"'}

povlhp commented 4 months ago

And some data on my device

om_components.sunseeker.sunseeker] MQTT message: /app/xxx 1/get {"result": true, "cd": 400,"deviceSn":"xxx 19", "command": 102} 2024-04-21 07:41:38.342 DEBUG (Thread-3 (_thread_main)) [cust om_components.sunseeker] callback - Sunseeker xxx 37919 data updated 2024-04-21 07:41:38.342 DEBUG (Thread-3 (_thread_main)) [cust om_components.sunseeker.sunseeker] MQTT message: /app/xxx1/get {"rain":1, "multizone":2, "language":0, "led" :0, "gps":0, "i nfo_stat":1, "deviceSn":"xxx","iot":1,"ultra" :0, "schedule":2, "password": 1, "service": 1, "zone_ex": 1, "cmd": 51 4, "region": 0, "sp":0, "map": 1} 2024-04-21 07:41:38.342 DEBUG (Thread-3 (_thread _main)) Icust om_components.sunseeker] callback - Sunseeker CD1216800077000 37919 data updated 2024-04-21 07:41:38.342 DEBUG (Thread-3 (_thread_main)) [cust om_components.sunseeker.sunseeker] MQTT message: /app/xxx/get {"bb":{"sv": 40901, "hv":210700}, "avail":"market_active", "mb": {"sv": 31456, "hv": 22500}, "1b":{"sv":0, "hv": 0}, "name": "Grasmus" ', "model": "RMA1013M20V-DNWNOS", "cmd": 508, "sn": "xxx", "btl": {"'sv";50517}, "version":31201, "deviceSn": "xxx", "db": {"'sv":61203, "hv":60300}}

povlhp commented 4 months ago

I have forked the project, and will add support for more sensors. I did a cat home-assistant.log | fgrep 'MQTT'|sed 's/^[^{]*{/{/'|jq 'keys'|sort|uniq

And found these keys in the JSON - I found no map. Number of times battery charged / discharged might be nice to have, as well as others. I will submit code back when I have added more sensors to my own fork.

Sdahl1234 commented 4 months ago

There are a lot of sensor im not exposing. I'm only exposing sensors I think is relevant. Just ask if you want other sensors exposed. You can even get the number of time your battery has be charged/decharged and the model/serialnumber of the battery. Even how many times it's been replaced. bluetoothMac and the time you registered the robot :-). I can get a list of possible sensors if you like?

povlhp commented 4 months ago

Map status is queried by GET /api/map/work-map/newest/1/$sn whenever I enter map tab in app. I assume it should be polled once every few minutes which returns metadata on the map {"code":0,"msg":null,"data":{"id":"1781953801190199298","mapSn":"2","deviceSn":"xxx","area":293,"borderLength":137406,"createTime":"2024-04-21 07:51:07"} Only when mapSn is updated, the full map is requested.

Map can de requested deleted in app. Deletes it on server as well DELETE /api/map/work-map/mobile/$sn followed by DELETE /api/map/work-map/del-map-callback?$sn

Specific map version is retrieved by: GET /api/map/work-map/all-info?deviceSn=$sn&mapSn=3

and data is as below. App displays data[0].area as area in m2 and data[0].borderLength/1000 as length in meters, thus it is display raw in mm.

I have no idea about message format. data[0].workMaps[0]. maparea = 1133255182 I don't really have any idea how this is measured.

Cutting area is about 22m wide, and 25m heigh according to quick check on google maps pointX range is [-1535,743] = 2278 pointY range is [-1397, 1321] = 2718 Thus we can see that pointX and pointY values are likely centimeters from dock position.

Not sure if I will get obstaclesNum > 0 without Ultrasonic. Zone entry points are not in the map data, they will have to be calculated moving along the line and displayed as overlay. Charging base is at (0,0)

{
  "code": 0,
  "msg": null,
  "data": [
    {
      "id": "1782438414979768322",
      "mapSn": "3",
      "deviceSn": "xxxsnxxx",
      "area": 280,
      "borderLength": 137551,
      "createTime": "2024-04-22 15:56:48",
      "workMaps": [
        {
          "id": "1782438415017517057",
          "deviceSn": "xxxsnxxx",
          "mapSn": "3",
          "dataLength": 92,
          "generateTime": "2024-04-22 17:56:41",
          "obstacleNum": 0,
          "createTime": "2024-04-22 15:56:48",
          "message": "010102005c000400010000010002030000004f1902000e1a8c43d9a426660134000000001300060900008a63253d8eca7f3f8eca7fbf8a63253d34445246a8ae3046d0f288c572c9f2c2a05f002068340000e8290020a0290020002a0020373a3536",
          "serialNumber": 2,
          "dataType": "0",
          "protocolVersion": 4,
          "mapSerialNumber": 1,
          "islandNum": 0,
          "chargingPile": "1",
          "reservedBit": 0,
          "sendPkgCount": 2,
          "borderLength": "137551",
          "mapArea": "1133255182",
          "debugType": 1,
          "originalMessage": "[52, 0, 0, 0, 0, 19, 0, 6, 9, 0, 0, -118, 99, 37, 61, -114, -54, 127, 63, -114, -54, 127, -65, -118, 99, 37, 61, 52, 68, 82, 70, -88, -82, 48, 70, -48, -14, -120, -59, 114, -55, -14, -62, -96, 95, 0, 32, 104, 52, 0, 0, -24, 41, 0, 32, -96, 41, 0, 32, 0, 42, 0, 32, 55, 58, 53]",
          "subjectList": [
            {
              "id": "1782438415046877186",
              "workMapId": "1782438415017517057",
              "mapSn": "3",
              "dataLength": 180,
              "pointType": "0",
              "createTime": "2024-04-22 15:56:48",
              "updateTime": null,
              "delFlag": 0,
              "deviceSn": "xxxsnxxx",
              "serialNumber": 3,
              "totalGroups": 1,
              "currentGroup": 1,
              "totalPoint": 42,
              "currentPoint": 42,
              "pointList": [
                {
                  "id": "1782438415076237314",
                  "seq": 1,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 0,
                  "pointY": 3
                },
                {
                  "id": "1782438415076237315",
                  "seq": 2,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 251,
                  "pointY": 22
                },
                {
                  "id": "1782438415076237316",
                  "seq": 3,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 265,
                  "pointY": 102
                },
                {
                  "id": "1782438415076237317",
                  "seq": 4,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 249,
                  "pointY": 262
                },
                {
                  "id": "1782438415076237318",
                  "seq": 5,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -35,
                  "pointY": 283
                },
                {
                  "id": "1782438415076237319",
                  "seq": 6,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -85,
                  "pointY": 304
                },
                {
                  "id": "1782438415076237320",
                  "seq": 7,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -480,
                  "pointY": 319
                },
                {
                  "id": "1782438415076237321",
                  "seq": 8,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -618,
                  "pointY": 297
                },
                {
                  "id": "1782438415076237322",
                  "seq": 9,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -657,
                  "pointY": 243
                },
                {
                  "id": "1782438415076237323",
                  "seq": 10,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1060,
                  "pointY": 265
                },
                {
                  "id": "1782438415076237324",
                  "seq": 11,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1514,
                  "pointY": 747
                },
                {
                  "id": "1782438415076237325",
                  "seq": 12,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1535,
                  "pointY": 827
                },
                {
                  "id": "1782438415076237326",
                  "seq": 13,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1414,
                  "pointY": 915
                },
                {
                  "id": "1782438415076237327",
                  "seq": 14,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1437,
                  "pointY": 1020
                },
                {
                  "id": "1782438415076237328",
                  "seq": 15,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1487,
                  "pointY": 1072
                },
                {
                  "id": "1782438415076237329",
                  "seq": 16,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1465,
                  "pointY": 1103
                },
                {
                  "id": "1782438415076237330",
                  "seq": 17,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -116,
                  "pointY": 1321
                },
                {
                  "id": "1782438415076237331",
                  "seq": 18,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 624,
                  "pointY": 1262
                },
                {
                  "id": "1782438415076237332",
                  "seq": 19,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 734,
                  "pointY": 1242
                },
                {
                  "id": "1782438415076237333",
                  "seq": 20,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 743,
                  "pointY": 1230
                },
                {
                  "id": "1782438415076237334",
                  "seq": 21,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 691,
                  "pointY": -51
                },
                {
                  "id": "1782438415076237335",
                  "seq": 22,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 591,
                  "pointY": -60
                },
                {
                  "id": "1782438415076237336",
                  "seq": 23,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 546,
                  "pointY": -102
                },
                {
                  "id": "1782438415076237337",
                  "seq": 24,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 366,
                  "pointY": -128
                },
                {
                  "id": "1782438415076237338",
                  "seq": 25,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 294,
                  "pointY": -845
                },
                {
                  "id": "1782438415076237339",
                  "seq": 26,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 365,
                  "pointY": -860
                },
                {
                  "id": "1782438415076237340",
                  "seq": 27,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 620,
                  "pointY": -856
                },
                {
                  "id": "1782438415076237341",
                  "seq": 28,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 627,
                  "pointY": -927
                },
                {
                  "id": "1782438415076237342",
                  "seq": 29,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 584,
                  "pointY": -1378
                },
                {
                  "id": "1782438415076237343",
                  "seq": 30,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 519,
                  "pointY": -1397
                },
                {
                  "id": "1782438415076237344",
                  "seq": 31,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1426,
                  "pointY": -1181
                },
                {
                  "id": "1782438415076237345",
                  "seq": 32,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1415,
                  "pointY": -1056
                },
                {
                  "id": "1782438415076237346",
                  "seq": 33,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -1345,
                  "pointY": -1044
                },
                {
                  "id": "1782438415076237347",
                  "seq": 34,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -644,
                  "pointY": -1159
                },
                {
                  "id": "1782438415076237348",
                  "seq": 35,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 37,
                  "pointY": -1258
                },
                {
                  "id": "1782438415076237349",
                  "seq": 36,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 72,
                  "pointY": -1156
                },
                {
                  "id": "1782438415076237350",
                  "seq": 37,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 230,
                  "pointY": -255
                },
                {
                  "id": "1782438415076237351",
                  "seq": 38,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 146,
                  "pointY": -205
                },
                {
                  "id": "1782438415076237352",
                  "seq": 39,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -33,
                  "pointY": -181
                },
                {
                  "id": "1782438415076237353",
                  "seq": 40,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -109,
                  "pointY": -140
                },
                {
                  "id": "1782438415076237354",
                  "seq": 41,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": -90,
                  "pointY": -93
                },
                {
                  "id": "1782438415076237355",
                  "seq": 42,
                  "deviceSn": "xxxsnxxx",
                  "mapSn": "3",
                  "pointX": 0,
                  "pointY": -2
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "ok": true
}
Sdahl1234 commented 4 months ago

Just a quick note. You have to find another way instead of pooling. You will be banned (and risk they block the integration) Check if the data is in the mqtt messages instead.

povlhp commented 4 months ago

Some sensors like battery makes sense.

Map metadata is queried once in a while - together with query for mower state - and if map updated (mapSn changes) another call is made to retrieve the map.

Exposing a way to call check and update the map would be nice. I am working on it. Map status is where you get perimeter length and area as well. Area will be sent to MQTT as well after query. Map update (if updated) from an automation could run once a day.

Distance to zone start and zone time % are sent to MQTT when updated. Not sure if there is a more passive way to get it.

I think some sensors should be suppressed unless available. Like the map stuff.

povlhp commented 4 months ago

And relevant values that should be exposed are mul_pro1..mul_pro4 - they set the percentage of time that the mower should use that zone starting point. Thus you can set it to start at the backyard 10% of the time, one entry point where it rarely goes 20% of the time, and let the others at 0% (aka disabled), and it would likely start those at the base. Right now you force 25% at each zone point, destroying settings already set thru the app.

The meters are nice, but could be read only. Not sure if you need to submit them. Meters/percent covers the same.

Lawn area should be pretty static - ony updates with map aka border cut I think, but would be nice to have. But it is updated only in MQTT when the map version is requested (by GET /api/map/work-map/newest/1/$sn ) MQTT: {"area":290,"ver":4,"cmd":522,"deviceSn":"CD121680007700037919"}

An exposed service to update values would be nice. Should poll data. And a comment not to call it often. Mapdata should be retrieved as part of this if map available. And could easily be rendered in SVG form as a path.

Sdahl1234 commented 4 months ago

Can you set the mul_pro1..4 in the app?. I don't have the settings, and is always set to 25% each. I will give it a go and see if I can make my robot uses the values. All settings value is relevant :-)

I need to submit all. Meter, mul_pro and start percentage.

Area covered are updated every time the battery status change on my end.

povlhp commented 4 months ago

My Clever 1000 has the map. And below the map I can set frequency percent - one does not influence the other. Thus I can have all at 40%.

Meter might be a calculated value. When I move a spot along the wire percent slider is moved as well and vice versa.

Sdahl1234 commented 4 months ago

My mower does not care about the mul_pro values. I can set mul_pro1 to 10% and it reads back 25. I have added the mul_pro1...4 as number entities. They are now used instead of the hardcoded 25. so updating zone values does not break your settings.

Can you please post you mqtt message with mul_pro values, so i can be sure to read the correct values, because i don't get them.

I will do some more testing before commiting.

image

Sdahl1234 commented 4 months ago

Sorry. You posted it earlier. "mul_pro1".

Sdahl1234 commented 4 months ago

More unparsed data on mine. On_min/total_min is the same on mine. It is total time in minutes. One of them should be exposed.

Cur_area seems to be increasing when mode = 1. Not sure about units. Could be 10* m2 mowed. Is 42 after 1 min - 210 after 5 min.

With 1.2km/h = 0.33m/s we have max 18m/min. Multiply by 0.2m cutting diameter that is theoretical max of 3.2m2/min. My guess is that we are closer to 0.42m2 when it says 42.

After 60 minutes it says 2520. Thus likely 25.2m2.

After mowing 228 minutes it says 9492. Almost 4hours and less than 100m2 ?? I think the number might be too low. Say average is 3m before hitting something, that is 10s, then 5s to stop/turn be up to speed. It should have a cutting speed of 2/3 or at least half of max.

Guess I have to do something manual. Say measure 10 or 20 meters. Let it cut/move straight and see result.

2024-04-21 10:52:14.388 DEBUG (Thread-3 (_thread _main)) [cust om_components.sunseeker.sunseeker] MQTT message: /app/xxx1/get {"mode": 1, "cur_min":60, "cur _area":2520, "wifi_lv":3, "on min": 1235, "station"; false, "cmd":501, "power": 74, "total_min": 1235, "on_area":889, "deviceSn":"xxx"'}

I wanted to expose the values, but not knowing what they mean does not make any sense. on_area is updated when cur_area is reset. {"mode":1,"cur_min":95,"cur_area":3591,"wifi_lv":3,"on_min":139715,"station":false,"cmd":501,"power":19,"total_min":139715,"on_area":139461,"deviceSn":"xxxx"} {"mode":2,"cur_min":0,"cur_area":0,"wifi_lv":3,"on_min":139716,"station":false,"cmd":501,"power":19,"total_min":139716,"on_area":139820,"deviceSn":"xxxx"}

on_area 139820 - 139461 = 359 cur_area = 3591 This indicates that cur_area might be 359,1 units.

I have 3591 units in 95 min. = 2268 in 60 minutes. It might be meters. 226,8 meters with width of 18cm = 226,8 * 0.18 = 40,8 m2/hour And yours is 252,0 meters with width of 20 cm = 50,4 m2/hour ???

povlhp commented 4 months ago

Here is a screenshot of the app where I can change the time settings for the individual zones. Changing startpoint moves the zone dots on the map.

download

Here is raw MQTT log of me going to the map and adjusting some frequencies. As written above, the first line is the result of the map statuscheck that return the area, it is called everytime I switch to mapview.

{"area":290,"ver":4,"cmd":522,"deviceSn":"xxxsnxxx"}
{"result":true,"cmd":400,"deviceSn":"xxxsnxxx","command":108}
{"mul_meter1":1000,"mul_meter2":23272,"mul_meter3":43807,"mul_meter4":101304,"mul_en":true,"mul_auto":false,"mul_zon1":0,"mul_mode":0,"mul_zon2":17,"mul_pro1":25,"mul_zon3":32,"deviceSn":"xxxsnxxx","mul_zon4":74,"mul_pro4":15,"mul_pro3":25,"mul_pro2":15,"cmd":507}
{"mul_meter1":1000,"mul_meter2":23272,"mul_meter3":43807,"mul_meter4":101304,"mul_en":true,"mul_auto":false,"mul_zon1":0,"mul_mode":0,"mul_zon2":17,"mul_pro1":25,"mul_zon3":32,"deviceSn":"xxxsnxxx","mul_zon4":74,"mul_pro4":15,"mul_pro3":25,"mul_pro2":15,"cmd":507}
povlhp commented 4 months ago

As for on_area / cur_area, they are either measured or guessed/estimated areas (since on / current mowing process). Your 2260/hour = 37.8/min, where mine is 42. Thus your value is exactly 10% lower, explained by my cutting disk being 20cm and yours 10% smaller at 18cm. Thus it is an area (gu)estimate, but since it looks VERY linear when I see the graph in HA - it might not be a measured value.

0.35m/s 60s 0.2m = 4.2m2 per minute. All too nice values to be a coincidence. And the 1.2km/h in the manual is 0.35m/s instead. But it is clear, that hitting obstacles or wire, stopping, turning, accelerating make it impossible to keep this speed. They could at some point take this into account. say pause their time component when not moving forward.