alistair23 / AutoMower-BLE

This is an unofficial reverse engineered Husqvarna Automower Connect BLE library. This allows connecting and controlling an Automower without any accounts, cloud or network connection.
GNU General Public License v3.0
82 stars 23 forks source link

Easilife Go 500 - does not accept any commands #53

Closed andyb2000 closed 2 months ago

andyb2000 commented 4 months ago

This is for my Flymo Easilife go 500. Communication works, pairing, etc, so I can get status from the mower.py, status, serial number, etc. Sending commands, all of them fail with the following error: ERROR: Response failed validation

Here are some outputs trying various command controls:

Sending command to control mower (park)
command=park
2024-05-17 17:25:15,886 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af321205000000c703'
2024-05-17 17:25:15,972 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01af32120500050000d8'
2024-05-17 17:25:15,974 automower_ble.protocol INFO: Received: b'03'
2024-05-17 17:25:15,974 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01af32120500050000d803'
2024-05-17 17:25:15,974 __main__ ERROR: Response failed validation
command result = None
Sending command to control mower (resume)
command=resume
2024-05-17 17:28:11,719 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea11040000006303'
2024-05-17 17:28:11,804 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01afea11040005000016'
2024-05-17 17:28:11,805 automower_ble.protocol INFO: Received: b'03'
2024-05-17 17:28:11,805 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01afea1104000500001603'
2024-05-17 17:28:11,806 __main__ ERROR: Response failed validation
command result = None

Any suggestions on what to try next? Do the Writing/Received bytes look correct, the b'03' seems an unusual reply? What can I do for next steps please.

erikmol commented 4 months ago

According to this comment both of your request get the response of NOT_ALLOWED. What is the ModeOfOperation and MowerState?

The b03 is just the last chunk of the response. If the response is too long, it's split into multiple packet chunks.

andyb2000 commented 4 months ago

thanks for clarification on b03.

NOT_ALLOWED might be onto something as I'm often seeing "MowerState.RESTRICTED" in the HA component replies to state. I've added a getModeOfOperation to output in the library test. So this is 0:

python3 -m automower_ble.mower --address 60:98:66:83:69:C5
2024-05-17 20:27:11,053 automower_ble.protocol INFO: starting scan...
2024-05-17 20:27:11,054 automower_ble.protocol INFO: connecting to device...
2024-05-17 20:27:16,457 automower_ble.protocol INFO: connected
2024-05-17 20:27:16,457 automower_ble.protocol INFO: pairing device...
2024-05-17 20:27:16,461 automower_ble.protocol INFO: paired
2024-05-17 20:27:16,462 automower_ble.protocol INFO: [Service] 98bd0001-0b0e-421a-84e5-ddbf75dc6de4 (Handle: 9): Unknown
2024-05-17 20:27:21,713 automower_ble.protocol INFO: Writing: b'02fd160000000000002e14b63b6047000000004d61696e001203'
2024-05-17 20:27:21,928 automower_ble.protocol INFO: Received: b'02fd0d0000000000006315b63b6047b603'
2024-05-17 20:27:21,929 automower_ble.protocol INFO: Final response: b'02fd0d0000000000006315b63b6047b603'
2024-05-17 20:27:21,929 automower_ble.protocol INFO: Writing: b'02fd0a00b63b6047008f08012803'
2024-05-17 20:27:22,020 automower_ble.protocol INFO: Received: b'02fd0b00b63b604700b20901011403'
2024-05-17 20:27:22,021 automower_ble.protocol INFO: Final response: b'02fd0b00b63b604700b20901011403'
2024-05-17 20:27:22,021 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af5a1209000000b703'
2024-05-17 20:27:22,108 automower_ble.protocol INFO: Received: b'02fd1300b63b604701e601af5a1209000002001e'
2024-05-17 20:27:22,110 automower_ble.protocol INFO: Received: b'051b03'
2024-05-17 20:27:22,110 automower_ble.protocol INFO: Final response: b'02fd1300b63b604701e601af5a1209000002001e051b03'
Mower manufacturer: Flymo
2024-05-17 20:27:22,111 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af5a1209000000b703'
2024-05-17 20:27:22,201 automower_ble.protocol INFO: Received: b'02fd1300b63b604701e601af5a1209000002001e'
2024-05-17 20:27:22,202 automower_ble.protocol INFO: Received: b'051b03'
2024-05-17 20:27:22,202 automower_ble.protocol INFO: Final response: b'02fd1300b63b604701e601af5a1209000002001e051b03'
Mower model: Easilife Go 500
2024-05-17 20:27:22,203 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af0a10150000003003'
2024-05-17 20:27:22,288 automower_ble.protocol INFO: Received: b'02fd1200b63b604701db01af0a10150000010001'
2024-05-17 20:27:22,291 automower_ble.protocol INFO: Received: b'1603'
2024-05-17 20:27:22,291 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01af0a101500000100011603'
Mower is charging
2024-05-17 20:27:22,292 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af0a1014000000bf03'
2024-05-17 20:27:22,380 automower_ble.protocol INFO: Received: b'02fd1200b63b604701db01af0a10140000010062'
2024-05-17 20:27:22,382 automower_ble.protocol INFO: Received: b'a603'
2024-05-17 20:27:22,383 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01af0a10140000010062a603'
Battery is: 98%
2024-05-17 20:27:22,383 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea11020000006a03'
2024-05-17 20:27:22,468 automower_ble.protocol INFO: Received: b'02fd1200b63b604701db01afea11020000010007'
2024-05-17 20:27:22,470 automower_ble.protocol INFO: Received: b'5b03'
2024-05-17 20:27:22,470 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01afea110200000100075b03'
Mower state: MowerState.RESTRICTED
2024-05-17 20:27:22,471 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea1103000000e503'
2024-05-17 20:27:22,560 automower_ble.protocol INFO: Received: b'02fd1200b63b604701db01afea11030000010005'
2024-05-17 20:27:22,561 automower_ble.protocol INFO: Received: b'd003'
2024-05-17 20:27:22,561 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01afea11030000010005d003'
Mower activity: MowerActivity.PARKED
2024-05-17 20:27:22,562 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af321201000000c903'
2024-05-17 20:27:22,652 automower_ble.protocol INFO: Received: b'02fd1500b63b6047016801af3212010000040028'
2024-05-17 20:27:22,654 automower_ble.protocol INFO: Received: b'8348661503'
2024-05-17 20:27:22,654 automower_ble.protocol INFO: Final response: b'02fd1500b63b6047016801af32120100000400288348661503'
Next start time: 2024-05-18 10:30:00
2024-05-17 20:27:22,655 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af761200000000ef03'
2024-05-17 20:27:22,784 automower_ble.protocol INFO: Received: b'02fd2d00b63b6047018d01af76120000001c009b'
2024-05-17 20:27:22,785 automower_ble.protocol INFO: Received: b'ac230012e221003f910b00f19801009d26000018'
2024-05-17 20:27:22,787 automower_ble.protocol INFO: Received: b'01000012e221000203'
2024-05-17 20:27:22,787 automower_ble.protocol INFO: Final response: b'02fd2d00b63b6047018d01af76120000001c009bac230012e221003f910b00f19801009d2600001801000012e221000203'
totalRunningTime 2337947
totalCuttingTime 2220562
totalChargingTime 758079
totalSearchingTime 104689
numberOfCollisions 9885
numberOfChargingCycles 280
cuttingBladeUsageTime 2220562
2024-05-17 20:27:22,788 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100af5a120a0000003f03'
2024-05-17 20:27:22,920 automower_ble.protocol INFO: Received: b'02fd1500b63b6047016801af5a120a00000400d8'
2024-05-17 20:27:22,922 automower_ble.protocol INFO: Received: b'ef9c0c3803'
2024-05-17 20:27:22,923 automower_ble.protocol INFO: Final response: b'02fd1500b63b6047016801af5a120a00000400d8ef9c0c3803'
Serial number: 211611608
2024-05-17 20:27:22,923 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea1101000000e203'
2024-05-17 20:27:23,008 automower_ble.protocol INFO: Received: b'02fd1200b63b604701db01afea11010000010000'
2024-05-17 20:27:23,010 automower_ble.protocol INFO: Received: b'8103'
2024-05-17 20:27:23,011 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01afea110100000100008103'
ModeOfOperation: 0
2024-05-17 20:27:23,011 automower_ble.protocol INFO: Writing: b'02fd1400b63b6047015500af7a1201000400000000007903'
2024-05-17 20:27:23,148 automower_ble.protocol INFO: Received: b'02fd1a00b63b6047012a01af7a12010000090090'
2024-05-17 20:27:23,150 automower_ble.protocol INFO: Received: b'5246660100000001d903'
2024-05-17 20:27:23,150 automower_ble.protocol INFO: Final response: b'02fd1a00b63b6047012a01af7a120100000900905246660100000001d903'
Last message: 
        2024-05-16 18:38:08
        OUTSIDE_WORKING_AREA
2024-05-17 20:27:23,155 automower_ble.protocol INFO: disconnecting...
2024-05-17 20:27:25,663 automower_ble.protocol INFO: disconnected
erikmol commented 4 months ago

NOT_ALLOWED might be onto something as I'm often seeing "MowerState.RESTRICTED" in the HA component replies to state. I've added a getModeOfOperation to output in the library test. So this is 0

You have MowerState=RESTICTED, MowerActivity=PARKED and ModeOfOperation=AUTO which means that the mower is in auto (schedule) mode but currently parked and restricted due to schedule.

andyb2000 commented 4 months ago

Ah thanks @erikmol so when I send an override to it, it should set mode to manual. When I try to set ModeOfOperation.MANUAL it just won't take it:

2024-05-18 08:55:52,230 automower_ble.protocol INFO: Final response: b'02fd1200b63b604701db01afea110100000100008103'
ModeOfOperation: 0
Sending command to control mower (override)
command=override
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 299, in <module>
    asyncio.run(main(mower))
  File "/usr/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 244, in main
    cmd_result = await mower.mower_override()
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 128, in mower_override
    await self.set_parameter("setModeOfOperation", mode=ModeOfOperation.MANUAL)
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 38, in set_parameter
    await self.get_parameter(parameter_name, **kwargs)
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 46, in get_parameter
    request = command.generate_request(**kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/protocol.py", line 147, in generate_request
    request_data += kwargs[request_name].to_bytes(1, byteorder="little")
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'ModeOfOperation' object has no attribute 'to_bytes'

(I applied the fix in https://github.com/erikmol/AutoMower-BLE/commit/78b74bcdee9c8af93dcc16dae41613eef14b684a also to see if that would help but no luck)

erikmol commented 4 months ago

Try ModeOfOperation.MANUAL.value

andyb2000 commented 4 months ago

No luck unfortunately :-(

Setting:

self.set_parameter("setModeOfOperation", mode=ModeOfOperation.MANUAL.1)

Fails with syntax

  File "/home/browna/Development/AutoMower-BLE-alistair/automower_ble/mower.py", line 134
    await self.set_parameter("setModeOfOperation", mode=ModeOfOperation.MANUAL.1)
                                                        ^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: invalid syntax. Perhaps you forgot a comma?

So I tried:

self.set_parameter("setModeOfOperation", mode=1)

Which also fails as before:

ModeOfOperation: 0
Sending command to control mower (setmanual)
command=setmanual
2024-05-18 09:39:57,528 automower_ble.protocol INFO: Writing: b'02fd1100b63b6047019c00afea1100000100016d03'
2024-05-18 09:39:57,617 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01afea11000005000009'
2024-05-18 09:39:57,619 automower_ble.protocol INFO: Received: b'03'
2024-05-18 09:39:57,621 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01afea1100000500000903'
2024-05-18 09:39:57,621 __main__ ERROR: Response failed validation
command result = None
2024-05-18 09:39:57,621 automower_ble.protocol INFO: Writing: b'02fd1400b63b6047015500af7a1201000400000000007903'
2024-05-18 09:39:57,704 automower_ble.protocol INFO: Received: b'02fd1a00b63b6047012a01af7a12010000090090'
2024-05-18 09:39:57,707 automower_ble.protocol INFO: Received: b'5246660100000001d903'
2024-05-18 09:39:57,708 automower_ble.protocol INFO: Final response: b'02fd1a00b63b6047012a01af7a120100000900905246660100000001d903'
Last message: 
andyb2000 commented 4 months ago

I tried a few things over the weekend and cannot seem to get it to switch modeofoperation. Is there a code snippet you can suggest @erikmol that I can try to see if I'm getting it wrong from coding point of view? Thanks!

erikmol commented 4 months ago

Setting:

self.set_parameter("setModeOfOperation", mode=ModeOfOperation.MANUAL.1)

Fails with syntax

Yeah I was stating ModeOfOperation.MANUAL.value which will resolve to 1

self.set_parameter("setModeOfOperation", mode=1)

Yes this should be equivalent!

Looking at your log from the previous issue it seems to set the ModeOfOperation (4586, 0) to 0 (i.e. AUTO) before setting the override duration (4658, 3) to 3600. So you might not need to set it to manual mode after all?

There is also a requestTrigger (4586, 4) sent by the app which returns UNKNOWN_ERROR so it seems like we should be a bit graceful when parsing the responses.

I'm attaching a new parsed version of your btsnoop_hci.log: btsnoop_hci_decoded.txt. Getting more of these logs will help us understand more about the commands sent and in which order. (I don't have an android phone myself)

andyb2000 commented 4 months ago

makes sense, thanks for the reply. I'm away for a week so when I get back, should I do some more captures from my phone/bluetooth connection to gather more logs that we can take a look at? I struggle with the parsing/understanding of the protocol communication but can gather more info and see if it's possible to crack it! It feels so close. I managed to do another PR to push extra features into the Home Assistant integration to make it look/provide more information similar to the API driven Husqvarna integration: https://github.com/alistair23/homeassistant-core/pull/3

erikmol commented 3 months ago

Hi @andyb2000, I've done some more digging with the latest commits which also supports to read out the override state. I've used the following code:

    print("GetMode: " + str(await mower.command("GetMode")))
    print("GetState: " + str(await mower.command("GetState")))
    print("GetActivity: " + str(await mower.command("GetActivity")))
    override_dict = await mower.command("GetOverride")
    print("Override.action: " + str(override_dict['action']))

And found out the following:

  1. To park forever the SetMode command should be set to 2, i.e. ModeOfOperation.HOME
  2. To override mow the Mode should be 0 i.e. ModeOfOperation.AUTO (This confirms what I saw in your logs). The Override.action output will print 2 i.e. OverrideAction.FORCEDMOW.
  3. Parked until next start will have Mode 0/AUTO but Override.action output will print 1 i.e. OverrideAction.FORCEDPARK.

So I think the mower_override function might need the following sequence:

        await self.command("ClearOverride") # Or maybe this is not needed as SetOverrideMow is set below?
        await self.command("SetMode", mode=ModeOfOperation.AUTO)
        await self.command("SetOverrideMow", duration=duration_hours * 3600)

But when I tried it out my mower initially did not do anything, but a few minutes later it was mowing? So maybe there is also a need to await self.command("StartTrigger")? I saw this in your log after SetMode and SetOverrideMow.

Any comments @alistair23 ?

EDIT: please see #64

andyb2000 commented 3 months ago

Sorry I've been awol for a while, I need to take another look at this. I do suspect it's still a pairing issue that's taking place here. I think at the moment my unit is returning data due to a buggy BLE implementation, in that it will talk to anyone that isn't "fully" paired and give status data but to send commands requires full pairing (i.e. sending the pin). However when I've tested using the static pin it still doesn't appear to work.

I'm going to setup a test android app that I can try and debug more in-depth the initial bluetooth setup of the app/pairing and see if I can capture what's going on there.

For now, I've customised the library and home-assistant custom component so I'm at least getting state of the mower, battery, next cutting start time, etc, just without control.

erikmol commented 3 months ago

I think at the moment my unit is returning data due to a buggy BLE implementation, in that it will talk to anyone that isn't "fully" paired and give status data but to send commands requires full pairing (i.e. sending the pin). However when I've tested using the static pin it still doesn't appear to work.

You can try to see if the IsOperatorLoggedIn command returns anything interesting.

EDIT: IsOperatorLoggedIn returns 1 for me (Automower 305)

andyb2000 commented 3 months ago

Thanks! Good call, I've done that and definitely interesting result:

IsOperatorLoggedIn: 0

So, although it's returning data over the BLE link (i.e. I think the bluetooth layer is 'paired' it's not negotiated the pin pairing with the mower) so I think this further confirms what I'm thinking.

I'm going to try with pin code and see if I can get that to flip to a 1.

lasry1 commented 2 months ago

I believe with the Flymo Easilife Go when it first pairs to the app the pin is required on both, the lawnmower and the app to achieve the IsOperatorLoggedIn = 1

The default pin is buttons A, B, C, D , being to top, right, bottom, left. I believe this can't be removed but only changed to a different pin through the app but i haven't actually tried it

Are you able to log anything through the android phone while pairing and adding the pin code?

reidjr2 commented 2 months ago

Not sure if it means anything, but comparing andyb2000 various write/receive traces with my own results.

I have an easy life go 150. All of the successful reads with the same values have the same received strings. All The failed commands have exactly the same responses as https://github.com/alistair23/AutoMower-BLE/issues/53#issue-2303150308

Sending command to control mower (resume)
command=resume
2024-05-17 17:28:11,719 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea11040000006303'
2024-05-17 17:28:11,804 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01afea11040005000016'
2024-05-17 17:28:11,805 automower_ble.protocol INFO: Received: b'03'
2024-05-17 17:28:11,805 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01afea1104000500001603'
2024-05-17 17:28:11,806 __main__ ERROR: Response failed validation
command result = None

However if I set the pin with --pin=1342:

The working status received/and responces are the same, but for the failed commands the responses are different, even if they still fail.

Sending command to control mower (resume)
command=resume
2024-07-27 14:29:48,842 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea11040000006303'
2024-07-27 14:29:48,986 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01afea11040001000088'
2024-07-27 14:29:48,987 automower_ble.protocol INFO: Received: b'03'
2024-07-27 14:29:48,988 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01afea1104000100008803'
2024-07-27 14:29:48,988 __main__ WARNING: Response failed validation
command result = None

and with --pin=1234

Sending command to control mower (resume)
command=resume
2024-07-27 14:34:28,844 automower_ble.protocol INFO: Writing: b'02fd1000b63b604701a100afea11040000006303'
2024-07-27 14:34:28,942 automower_ble.protocol INFO: Received: b'02fd1100b63b6047019c01afea11040005000016'
2024-07-27 14:34:28,943 automower_ble.protocol INFO: Received: b'03'
2024-07-27 14:34:28,944 automower_ble.protocol INFO: Final response: b'02fd1100b63b6047019c01afea1104000500001603'
2024-07-27 14:34:28,944 __main__ WARNING: Response failed validation
command result = None

Does that suggest we are seeing some sort of challenge expecting another write due to the pin codes? And do other mowers which have PIN codes that are optional, work if the pin is set ?

reidjr2 commented 2 months ago

Success :-).

I have it working for me on an easylife go 150.

Ignore my previous comment, all the bits were already in place, thank you all :-).

For the easylife go 150:-

For the commands, you must use --pin={yourpin}, although sensors work without.

With the correct pin (where power/schedule/run/park represented by 1/2/3/4), then pause and resume work for me.

as https://github.com/alistair23/AutoMower-BLE/issues/53#issuecomment-2161581174

You need to send await self.command("StartTrigger") after override and park.

With both pin and StartTrigger after override/park, all the commands exposed by mower.py work for me.

You still get " WARNING: Response failed validation", and the connectivity and bluetooth range are pretty flakey, but thats not due to anything here ( I believe). If the commands complete ( bluetooth connectivity, mower stack awake) then the commands will succeed. I notice in the wireshark traces, liberal use of keepalives, that might be usefull in waking the mower (?)

For my own use i added this diff to mower.py to get park working, and to recheck state after command for testing.

142,143d141
<         # The response validation is expected to fail
<         await self.command("StartTrigger")
247,250c245
<     # Check change of state
<     state = await mower.mower_state()
<     if state is not None:
<         print("New Mower state: " + state.name)
---
> 

And then example: ~/virtual/bin/python3 -m automower_ble.psmower --address 60:98:66:F9:69:DA --pin=1342 --command park

Mower manufacturer: Flymo
Mower model: Easilife Go 150
Mower is not charging
Battery is: 100%
Mower state: PAUSED
Mower activity: NONE
Next start time: 2024-07-28 12:00:00
totalRunningTime 1640385
totalCuttingTime 1451828
totalChargingTime 125750
totalSearchingTime 123289
numberOfCollisions 8880
numberOfChargingCycles 133
cuttingBladeUsageTime 1459240
Serial number: 212400730
Mower name: Mo
Sending command to control mower (park)
command=park
command result = None
New Mower state: RESTRICTED
alistair23 commented 2 months ago

Do you want to open a PR with the fixes required?

lasry1 commented 2 months ago

Success :-).

I have it working for me on an easylife go 150.

Ignore my previous comment, all the bits were already in place, thank you all :-).

For the easylife go 150:-

For the commands, you must use --pin={yourpin}, although sensors work without.

With the correct pin (where power/schedule/run/park represented by 1/2/3/4), then pause and resume work for me.

as #53 (comment)

You need to send await self.command("StartTrigger") after override and park.

With both pin and StartTrigger after override/park, all the commands exposed by mower.py work for me.

You still get " WARNING: Response failed validation", and the connectivity and bluetooth range are pretty flakey, but thats not due to anything here ( I believe). If the commands complete ( bluetooth connectivity, mower stack awake) then the commands will succeed. I notice in the wireshark traces, liberal use of keepalives, that might be usefull in waking the mower (?)

For my own use i added this diff to mower.py to get park working, and to recheck state after command for testing.

142,143d141
<         # The response validation is expected to fail
<         await self.command("StartTrigger")
247,250c245
<     # Check change of state
<     state = await mower.mower_state()
<     if state is not None:
<         print("New Mower state: " + state.name)
---
> 

And then example: ~/virtual/bin/python3 -m automower_ble.psmower --address 60:98:66:F9:69:DA --pin=1342 --command park

Mower manufacturer: Flymo
Mower model: Easilife Go 150
Mower is not charging
Battery is: 100%
Mower state: PAUSED
Mower activity: NONE
Next start time: 2024-07-28 12:00:00
totalRunningTime 1640385
totalCuttingTime 1451828
totalChargingTime 125750
totalSearchingTime 123289
numberOfCollisions 8880
numberOfChargingCycles 133
cuttingBladeUsageTime 1459240
Serial number: 212400730
Mower name: Mo
Sending command to control mower (park)
command=park
command result = None
New Mower state: RESTRICTED

That's great!!

It would be ideal if, when prompted for the MAC address at the beginning of adding the mower, it also asked for a PIN. If a PIN is not needed, this field can be left blank

I'm using the custom repository at https://github.com/andyb2000/HACS-husqvarna_automower_ble, so I don't believe I can add my PIN directly in the code

reidjr2 commented 2 months ago

That's great!!

It would be ideal if, when prompted for the MAC address at the beginning of adding the mower, it also asked for a PIN. If a PIN is not needed, this field can be left blank

I'm using the custom repository at https://github.com/andyb2000/HACS-husqvarna_automower_ble, so I don't believe I can add my PIN directly in the code

Its a real hack, but will work. Find mower.py and add the changes manually. The library is used by the HA integration. /usr/local/lib/python3.12/site-packages/automower_ble/mower.py

You can hard code (your own) PIN with:

31c31
<     def __init__(self, channel_id: int, address, pin=1342):
---
>     def __init__(self, channel_id: int, address, pin=None):

While your there, add:

142,143d141
<         # The response validation is expected to fail
<         await self.command("StartTrigger")

to get Park working.

Or better still wait till the skilled people update the real code :-)

andyb2000 commented 2 months ago

Success! Great work thank you @reidjr2 mine now correctly shows IsOperatorLoggedIn: 1 after setting PIN and using that in the auth, I set it mowing and return to dock earlier and both worked correctly, so I'd say we can close this issue now.

Excellent result and for the library @alistair23 as it's now solved so many problems for me!