Stihl Imow - Robotic lawn mower

Looks like I got another hint. With the version 0.1.2 I got the timeout as well. Maybe it’s related to the updated imow-webapi dependency…

Hi Chris,
Not sure what issues you tackled in this release. But the two commands “startMowingFromPoint” still have errors.
Without duration or startpoint, the error I get is:
"Failed to call service stihl_imow.intent. 403, message, ‘Forbidden’, url=URL(‘https://api.imow.stihl.com/mower-actions/’)

With duration and startpoint provided in data, the error I get:
Failed to call service stihl_imow.intent. Expected str for dictionary value @data [‘duration’]. If I put duration in quotes I get “Unknown error” like before.

In the previous version the mower stays online 100% of the time in STD setting which is good. I only get a couple of data gaps, with regular updates of battery level.

Let me know what additional test/debug info I can provide.

In the meantime I have downloaded and installed ChrisHaPunkt/stihl-imow-webapi.
I have run the tests and I get the same error codes. So I guess the problem lies in the webapi, rather than in the integration. I am no expert in python and testing api’s so I will just post one of the logs here:

__________________________________________________ TestIMowApiOnlineIntegration.test_intent_start_mowing_with_defaults ___________________________________________________

self = <tests.test_integration_imow.TestIMowApiOnlineIntegration testMethod=test_intent_start_mowing_with_defaults>

def test_intent_start_mowing_with_defaults(self):
    result = self.loop.run_until_complete(
      self.imow.intent(IMowActions.START_MOWING, mower_id=self.test_mower.id)
    )

tests/test_integration_imow.py:107:


/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py:587: in run_until_complete
return future.result()
imow/api/init.py:351: in intent
response = await self.api_request(url, “POST”, payload=payload)
imow/api/init.py:289: in api_request
raise e
imow/api/init.py:282: in api_request
method, url, headers=headers_obj, data=payload
…/venv/lib/python3.7/site-packages/aiohttp/client.py:625: in _request
resp.raise_for_status()


self = <ClientResponse(https://api.imow.stihl.com/mower-actions/) [403 Forbidden]>
<CIMultiDictProxy(‘Cache-Control’: ‘no-cac…f’, ‘X-Xss-Protection’: ‘1; mode=block’, ‘Access-Control-Allow-Origin’: ‘*’, ‘Date’: ‘Sun, 18 Jul 2021 15:22:19 GMT’)>

def raise_for_status(self) -> None:
    if 400 <= self.status:
        # reason should always be not None for a started response
        assert self.reason is not None
        self.release()
        raise ClientResponseError(
            self.request_info,
            self.history,
            status=self.status,
            message=self.reason,
          headers=self.headers,
        )

E aiohttp.client_exceptions.ClientResponseError: 403, message=‘Forbidden’, url=URL(‘https://api.imow.stihl.com/mower-actions/’)

…/venv/lib/python3.7/site-packages/aiohttp/client_reqrep.py:1005: ClientResponseError
--------------------------------------------------------------------------- Captured log call ----------------------------------------------------------------------------
DEBUG imow:init.py:443 get_mower_action_id_from_id: 29137
DEBUG imow:init.py:481 receive_mower: 29137
DEBUG imow:init.py:486 <imow.common.mowerstate.MowerState object at 0x10cef95d0>
DEBUG imow:init.py:347 Intend: {‘actionName’: ‘startMowingFromPoint’, ‘actionValue’: ‘0000000441132897,3,0’}
======================================================================== short test summary info =========================================================================
FAILED tests/test_integration_imow.py::TestIMowApiOnlineIntegration::test_intent_start_mowing - aiohttp.client_exceptions.ClientResponseError: 403, message=‘Forbidden’…
FAILED tests/test_integration_imow.py::TestIMowApiOnlineIntegration::test_intent_start_mowing_with_defaults - aiohttp.client_exceptions.ClientResponseError: 403, messa…
====================================================================== 2 failed, 11 passed in 7.52s ================

Is this useful at all?

I have done some more tests intercepting the Stihl iMow App traffic.
When I send the command to go back to the docking station, the App sends a json file with the following contents:
{"actionValue":"secret_external_id","actionName":"toDocking"}
This command already worked.

When I send the command to just start mowing, without selecting a start and end time, the json that is sent is as follows. The time that is in the json is provided by the App:
{"actionValue":"secret_external_id, 2021-07-19 21:10", "actionName": "startMowing"}
Works from the App, but not from the api.

When I send the start mowing command from the App with begin and end time, the json is as follows:
{"actionValue":"secret_external_id, 2021-07-19 21:40, 2021-07-19 19:40", "actionName":"startMowing"}
Works from the App, but not from the api.

My iMow App actually does not provide the option to submit a startpoint at all. This may be because I have a different mower (632C) than you. Could it be that for this model the way the mow command is sent is different than other models?

I also intercepted the following response listing the features matrices. Perhaps there are some clues in there. It struck me as odd that the start mowing command was listed in this set of features. Because the other commands are entirely meant to change settings as you can see. Perhaps the start mowing command in the api needs to be structured in a similar way:



Hi @Jan_Willem_Maas , many thanks for your ongoing testing.
I know I’m behind your efforts and investigation, but I did not forget any of this. Gone weeks I’ve been very busy, but I’m still on the issues.
I just can ask for a little patience to you all.

Thanks so far

Know how it is! We do this on top of a lot of other things. No rush at all.

Very interesting, this helps. It looks like your actionValue needs to be provided in another way then mine ( for a 422PC)

the action Object I have to provide, for my mower (422PC) is

{
  "actionValue":"secret_external_id, <Duration>, <Startpoint>", 
  "actionName": "startMowingFromPoint"
}

and yours

{
  "actionValue":"secret_external_id, <StartingTime>, <EndTime>", 
  "actionName": "startMowing"
}

The release rc20 comes with disabled input field validation. So, in theory, the following service call should work after upgrading to rc20 for you. You would need to provide the values your mower needs in the corresponding fields: (duration=starttime, startpoint=endtime)

service: stihl_imow.intent
data:
  action: startMowingFromPoint
  mower_name: Mährlin
  duration: '2021-07-19 21:40' 
  startpoint: '2021-07-19 21:40'

Looking forward to your feedback.

Greetz

Hi @Red1 ,

I still have an ongoing ticket regarding the timeout problems with Stihl.
I was running the rc18 version for quite some time now (interval of 120s) without the timeout problem. Yesterday I updated the webapi back to the latest, still working fine.
Maybe they did something on the server side in between, but the problem seems to be gone.

@ ChrisHaPunkt looks like you have done a great deal of work on this. I am looking to invest in a Stihl RMI 632 PC - GPS Assisted iMOW® and would love to hear the views of what you guys think of the actual units? I have 2500m2 to 3000m2 to cut so think I need this model if you feel there would be a better model I would be interrested to hear your views. I have started to look to get the intergration into HACS but I am being thick as I cannot add the intergration to HACS. I assumed that all I needed to do was to go into HACS and click the add button and then paste in https://github.com/ChrisHaPunkt/stihl-imow-webapi however it comes back with an error. Can anybody point me in the right direction please?

thanks

Reply to machine model question:
We have about 1600 m2 to cut and have opted for the 632. Mainly because it is recommended to not operate the mower at night (to avoid nocturnal animals) or early in the morning when the grass is still wet. It runs 19 hours per week and for these hours it needs to charge for another 11 hours or so. The daily mowing time window is from 11.00 to 21.00. On average it does 3 mow cycles per day. This leaves ample time to avoid rainy days. If you size the machine too small it will need to run all hours of the day.

1 Like

thanks - I had not considered half of what you have mentioned and it makes sence. I was going to set it to mow at night as I have cats and dogs who will use the area in the day. I need to play with the intergration if I can get it to load in HA as my pets have GPS trackers on them so could stop the unit if they are out.

I now have the intergration installed it just work this time where as it did not before

We have a dog. We have taught her not to play with the robot and she does not. I do not think your cats will play with it but of course you can observe what happens the first few times.

Hi Pete, just saw your question about integrate the repo into HACS.
Actually you need to add the repo containing the integration:

You tried to add the repo containing the underlying webapi the integration is using.

thanks for getting back to me – I ended up buying a Husqvarna 415x as they are doing free fitting and burry the cable and as I have a dog who runs around thought a pegged cable would be ripped up.

Hi @ChrisHaPunkt,

i was happy that I found a plugin to integrate my iMow into home assistant. So i installed your plugin with HACS (v0.1.3). For first view everything looks fine without the function startMovingFromPoint.
I also have a iMow 422 PC.

If i try to check function with developer tools:

service: stihl_imow.intent
data:
action: startMowingFromPoint
mower_name: imow
duration: ‘90’

i get the errors

Error handling message: Unknown error (unknown_error)
403, message='Forbidden', url=URL('https://api.imow.stihl.com/mower-actions/')

I tried also the solution you recommended @jan_willem_maas for his iMow 632

service: stihl_imow.intent
data:
action: startMowingFromPoint
mower_name: imow
duration: ‘2022-05-20 15:40’
startpoint: ‘2022-05-20 15:50’

here i got this messages:

Error handling message: Unknown error (unknown_error)
websocket_api script: Error executing script. Unexpected error for call_service at pos 1: 422, message='Unprocessable Content', url=URL('https://api.imow.stihl.com/mower-actions/')
websocket_api script: Error executing script. Unexpected error for call_service at pos 1: 403, message='Forbidden', url=URL('https://api.imow.stihl.com/mower-actions/')

Any idea what’s wrong?
The thread is a bit older, does it still work for you?

Thanks
Micha

Hi,

can you provide more informations about how you bring the datapoints from the Stihl imow into nodered? Maybe you can post the flow? Thank you

BR Stefan

Is there a way to set the starting points (1 to 4), distance and frequency?

I use the imow integration to get the sensors that are defined by the integration. Then I use the following NodeRed flows:

[{"id":"40b6e380.0bb2d4","type":"tab","label":"Stihl imow","disabled":false,"info":""},{"id":"57adfd26.12e2cc","type":"switch","z":"40b6e380.0bb2d4","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"num"},{"t":"eq","v":"1","vt":"num"}],"checkall":"true","repair":false,"outputs":2,"x":290,"y":80,"wires":[["fa8af02e.299778"],["25bdf79d.9a2438"]]},{"id":"25bdf79d.9a2438","type":"change","z":"40b6e380.0bb2d4","name":"STD","rules":[{"t":"set","p":"payload","pt":"msg","to":"STD","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":100,"wires":[["187ab50c.a13cb3"]]},{"id":"fa8af02e.299778","type":"change","z":"40b6e380.0bb2d4","name":"ECO","rules":[{"t":"set","p":"payload","pt":"msg","to":"ECO","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":470,"y":60,"wires":[["187ab50c.a13cb3"]]},{"id":"187ab50c.a13cb3","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow Energy Mode","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"iMow Energy Mode"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":510,"y":160,"wires":[[]]},{"id":"a7a24a83.0dfd1","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get Energy mode","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"60","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.toon_energymode","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":110,"y":80,"wires":[["57adfd26.12e2cc"]]},{"id":"48e4c6b8.7235d","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get blade time","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.toon_statistics_totalbladeoperatingtime","state_type":"num","halt_if":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"x":120,"y":420,"wires":[["16162d86.849eda"]]},{"id":"16162d86.849eda","type":"function","z":"40b6e380.0bb2d4","name":"Secs -> hrs","func":"msg.payload = (msg.payload/3600).toFixed(1)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":420,"wires":[["9943b151.af4898"]]},{"id":"9943b151.af4898","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow Total Blade Operating Time","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"iMow Total Blade Operating Time"},{"property":"device_class","value":""},{"property":"icon","value":"mdi:knife"},{"property":"unit_of_measurement","value":"hours"}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":540,"y":420,"wires":[[]]},{"id":"c1c7d53f.4a2a5","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow Total Operating Time","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"iMow Total Operating Time"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"hours"}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":520,"y":480,"wires":[[]]},{"id":"2bab4e52.64ca4a","type":"function","z":"40b6e380.0bb2d4","name":"Secs -> hrs","func":"msg.payload = (msg.payload/3600).toFixed(2)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":480,"wires":[["c1c7d53f.4a2a5"]]},{"id":"ca933baa.a686d","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get oper time","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.toon_statistics_totaloperatingtime","state_type":"num","halt_if":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"x":110,"y":480,"wires":[["2bab4e52.64ca4a"]]},{"id":"b5e43fa2.c0878","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow total distance travelled","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"imow_total_distance_travelled"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"km"}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":520,"y":540,"wires":[[]]},{"id":"cff910b.6ba1b7","type":"function","z":"40b6e380.0bb2d4","name":"m -> km","func":"msg.payload = (msg.payload/1000).toFixed(0)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":290,"y":540,"wires":[["b5e43fa2.c0878"]]},{"id":"2dc0be92.9ab302","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get total distance","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.toon_statistics_totaldistancetravelled","state_type":"num","halt_if":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"x":130,"y":540,"wires":[["cff910b.6ba1b7"]]},{"id":"464040cf.388ad","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get battery level in txt","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":true,"entity_id":"sensor.toon_status_chargelevel","state_type":"str","halt_if":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"x":140,"y":360,"wires":[["1d8c3d3f.002873"]]},{"id":"ba924419.f89698","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow toon battery level","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"iMow toon battery level"},{"property":"device_class","value":"Battery"},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"%"}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":510,"y":360,"wires":[[]]},{"id":"1d8c3d3f.002873","type":"change","z":"40b6e380.0bb2d4","name":"Convert to integer","rules":[{"t":"set","p":"payload","pt":"msg","to":"$number(payload)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":360,"wires":[["ba924419.f89698"]]},{"id":"e38a6435.8fb7e","type":"api-call-service","z":"40b6e380.0bb2d4","name":"Device tracker see","server":"fe008c49.a4548","version":3,"debugenabled":false,"service_domain":"device_tracker","service":"see","entityId":"","data":"{\"dev_id\":\"Toon\",\"gps\":[\"{{states.sensor.toon_coordinatelatitude.state}}\",\"{{states.sensor.toon_coordinatelongitude.state}}\"]}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":510,"y":760,"wires":[[]]},{"id":"27a60635.c8a5d2","type":"inject","z":"40b6e380.0bb2d4","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"30","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":150,"y":760,"wires":[["e38a6435.8fb7e"]]},{"id":"afcc7300.99445","type":"poll-state","z":"40b6e380.0bb2d4","name":"Get suggested activity time","server":"fe008c49.a4548","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"updateinterval":"30","updateIntervalType":"num","updateIntervalUnits":"seconds","outputinitially":true,"outputonchanged":false,"entity_id":"sensor.toon_smartlogic_suggestedactivitytime","state_type":"num","halt_if":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"x":160,"y":600,"wires":[["c8af90df.23db68"]]},{"id":"c8af90df.23db68","type":"function","z":"40b6e380.0bb2d4","name":"m -> km","func":"msg.payload = (msg.payload * -1).toFixed(0)\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":600,"wires":[["1c5d01d2.b2570e"]]},{"id":"1c5d01d2.b2570e","type":"ha-entity","z":"40b6e380.0bb2d4","name":"iMow sug activity time nr","server":"fe008c49.a4548","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"iMow suggested activity time nr"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":"min"}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":510,"y":600,"wires":[[]]},{"id":"c53dfe14a01e9f3a","type":"comment","z":"40b6e380.0bb2d4","name":"Teken iMow positie op de kaart","info":"","x":200,"y":720,"wires":[]},{"id":"fe008c49.a4548","type":"server","name":"Home Assistant main","version":2,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":"30"}]

As you can see I use the Poll State node to obtain the values from the HA sensors, which I then convert to useful information. After that I create a number of sensors to get them back into HA. These sensors are:

  • Energy mode
  • Battery level
  • Blade operating time (hrs)
  • Total operating time (hrs)
  • Total distance travelled (in km)
  • Suggested activity time (min)
  • iMow position on the map

I do not think you can change the distance or frequency by using the imow integration. I believe you can set the starting point by calling the following service: Stihl iMow Initiate an iMow Action to a Mower. This service allows you to set the following parameter:

  • Mower name
  • iMow Action (start mowing from point, edge mowing, return to dock)
  • Duration (30, 60, 90 min)
  • Starting point (depends on the number of starting points defined)