allenporter/pyrainbird

Hardware support: LNK2 Wifi Module

Opened this issue · 23 comments

When starting a zone on the controller an error occurs

Error: Status request failed with wrong response! Requested 01 but got 00: {‘type’: ‘NotAcknowledgeResponse’, ‘commandEcho’: 57, ‘NAKCode’: 4}

Log:

`Logger: homeassistant.helpers.script.websocket_api_script
Source: custom_components/rainbird/switch.py:96
Integration: Rainbird
First occurred: 21 mei 2023 om 12:42:48 (1 occurrences)
Last logged: 21 mei 2023 om 12:42:48

websocket_api script: Error executing script. Unexpected error for call_service at pos 1: Status request failed with wrong response! Requested 01 but got 00: {'type': 'NotAcknowledgeResponse', 'commandEcho': 57, 'NAKCode': 4}
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 452, in _async_step
await getattr(self, handler)()
File "/usr/src/homeassistant/homeassistant/helpers/script.py", line 685, in _async_call_service_step
await service_task
File "/usr/src/homeassistant/homeassistant/core.py", line 1849, in async_call
task.result()
File "/usr/src/homeassistant/homeassistant/core.py", line 1889, in _execute_service
await cast(Callable[[ServiceCall], Awaitable[None]], handler.job.target)(
File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 809, in handle_service
await service.entity_service_call(
File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 798, in entity_service_call
future.result() # pop exception if have
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 980, in async_request_call
await coro
File "/usr/src/homeassistant/homeassistant/helpers/service.py", line 838, in _handle_entity_call
await result
File "/config/custom_components/rainbird/switch.py", line 117, in async_start_zone
await self.async_turn_on(duration=zone_run_time)
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 1034, in async_turn_on
await self.hass.async_add_executor_job(ft.partial(self.turn_on, **kwargs))
File "/usr/local/lib/python3.10/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/config/custom_components/rainbird/switch.py", line 96, in turn_on
response = self._controller.irrigate_zone(int(self._zone), int(duration // 60))
File "/usr/local/lib/python3.10/site-packages/pyrainbird/init.py", line 136, in irrigate_zone
response = self._process_command(
File "/usr/local/lib/python3.10/site-packages/pyrainbird/init.py", line 202, in _process_command
response = self.command(cmd, *args)
File "/usr/local/lib/python3.10/site-packages/pyrainbird/init.py", line 188, in command
raise Exception(
Exception: Status request failed with wrong response! Requested 01 but got 00:
{'type': 'NotAcknowledgeResponse', 'commandEcho': 57, 'NAKCode': 4}`

Version Information:

Home Assistant 2023.5.3
Supervisor 2023.04.1
Operating System 10.1
Frontend-versie: 20230503.3 - latest

Rainbird:
ESP-RZXe
Firmware: 2.9
LNK2 WiFi Module

Could you please add support of this device into the integration.

If you can give examples of the app running the mitm proxy i'm happy to add support for these

I tried did but was not succesful doing it.

if you could tell me what and how to set up the mitm proxy then we can sort it out.

https://github.com/allenporter/pyrainbird/blob/main/CONTRIBUTING.md has a little bit of information. Happy to help if you have a more specific question/issue

I will give it a go today.

First of all I need to understand how I can setup the virtual environment on my windows machine. I found a MITM tutorial on the homeassistant forum that Im going to follow.

I recommend using mitm with the rainbird app for what it's worth to capture the traffic of valid requests/ responses.

PyTest results:
image

Next:
image

And then I'm lost, I open the rainbird app on my phone and start an irregation but nothing happens in the proxy.

I need to add the proxy between the rainbird controller, any suggestions?

You need to configure your smart phone to use your computer as a proxy, something like described here https://gaikwadchetan93.medium.com/monitoring-modifying-android-app-network-traffic-via-mitm-proxy-part-1-886f6324f705

Got up and running now, I'm capturing the traffic from my iphone to the controller.

From a terminal I run this: mitmproxy -s examples/mitm_rainbird.py

mitmproxy starts capturing all trafic.

When accessing the controller from the app, it captures for example this request:

Request:
{ "id": 1, "jsonrpc": "2.0", "method": "requestWeatherAndStatus", "params": { "Country": "NL", "StickId": "MY_STICKID", "ZipCode": "MY_ZIPCODE" } }

Response:
{ "result": { "Weather": { "city": "My_City", "timeZoneId": "Europe/Amsterdam", "forecast": [ {}, { "dateTime": 1689116400, "high": 23.0, "chanceofrain": 87, "precip": 0.0157, "low": 18.0, "icon": "10d", "description": "rain" }, { "dateTime": 1689202800, "high": 22.0, "chanceofrain": 80, "precip": 0.0315, "low": 17.0, "icon": "10d", "description": "rain" }, { "dateTime": 1689289200, "high": 21.0, "chanceofrain": 89, "precip": 0.1535, "low": 16.0, "icon": "10d", "description": "rain" }, { "dateTime": 1689375600, "high": 21.0, "chanceofrain": 88, "precip": 0.0512, "low": 16.0, "icon": "10d", "description": "rain" }, { "dateTime": 1689462000, "high": 22.0, "chanceofrain": 75, "precip": 0.0236, "low": 18.0, "icon": "10d", "description": "rain" }, { "dateTime": 1689548400, "high": 22.0, "chanceofrain": 0, "precip": 0.0, "low": 17.0, "icon": "01d", "description": "clear sky" } ], "location": "MY_ZIPCODE", "timeZoneRawOffset": 3600 }, "ForecastedRain": {}, "DuplicateMac": false, "StickId": "MY_STICKID", "Controller": {}, "ConnectedStatus": [ { "Status": 0, "CompanyId": 0, "Enabled": true, "Params": {}, "Name": "Alarm.com" }, { "Status": 0, "CompanyId": 1, "Enabled": true, "Params": {}, "Name": "Amazon Alexa" }, { "Status": 0, "CompanyId": 2, "Enabled": true, "Params": {}, "Name": "Google Assistant" }, { "Status": 0, "CompanyId": 3, "Enabled": false, "Params": {}, "Name": "Flume" } ] }, "id": 1, "jsonrpc": "2.0" }

But when I start an irrigation run it return a response that could not be parsed so mitmproxy falls back to raw.

Ok great! Now you've got the harness all setup. That first request is to their cloud API so it's simple to decide.

The requests to the rainbird need to be decoded with the password. You should see some environment variables in the mitm code that you need to set which should enable decoding of device packets.

Once you have that going you should see some partial decoding of the events, which we can then look closer at to figure out the exact way the need to be encoded / decoded into specific commands and responses.

RAINBIRD_PASSWORD is the environment variable needed for mitm to decode.

Setting the Rainbird_password in the python script and capturing its request/response still shows the same octet-stream that cant be decoded.

I scrolled through all response to find one or more that make some sense but they are all still in binary.

Ok I worry maybe the password didn't work. Can you share how you set the environment variable? (Minus the actual password)

In the examples/mitm_rainbird.py script, I just replace the value of the environment variable into my controller pwd.

image

So within the array between the two quotes I place my password.

Then I save the file and start mitmproxy using this command from a terminal:

mitmproxy -s examples/mitm_rainbird.py

OK if you do it that way then set passwd = "MY_WORKING_CONTROLLER_PASSWORD" otherwise its looking in the environment variables for your password which won't be set.

Done, sorry for the late response was on a vacation trip.

Changed the way of setting the pwd in the script, like you said.
Then I took the same steps and run the mitmproxy.

Still none understandable response. The one thing that is different now is that mitmproxy detects its HEX instead of falling back to raw.

Any advice ?

I'm not sure what you mean. is it worth sharing a little more detail? If its detecting as hex, perhaps its what we're looking for. You'll have a request and a response in hex that looks something like this: https://github.com/allenporter/pyrainbird/blob/main/tests/testdata/current_queue_me3.yaml

In that example file (and see others in that directory) you have the hex on top and the decoded message on the bottom, and so we want to start with the hex requests and figure out how to decode it (i can help do that)

StartedIrrigationRequest.txt
StartedIrrigationResponse.txt

This is the first request / response when I started an irrigation run for a zone.

OK wasn't exactly the format I was expecting, but i see what you mean about it being raw hex. I worry this is the encrypted payload. I'm having trouble understanding this hex dump in the context of what mitmproxy shows when i use it myself. Perhaps you can hare more detail about the command you run or screenshots if using it in interactive mode, etc.

@allenporter, any way I can send the screenshots and screen capture to you ?

You can attach to the issue or email allen.porter@gmail.com

salvoM commented

Hey guys can you please update the issue as I'm trying myself to understand how can i make it work with this LNK2 module.

@allenporter RAINBIRD_PASSWORD to is the local Access Point Wi-Fi password?

It's the password for the device so you can decode the packets intercepted by the proxy.