allenporter / pyrainbird

Rain Bird Controller in Python
https://allenporter.github.io/pyrainbird/
MIT License
59 stars 31 forks source link

Hardware support: LNK2 Wifi Module #178

Open DotNet2Web opened 1 year ago

DotNet2Web commented 1 year ago

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.

allenporter commented 1 year ago

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

DotNet2Web commented 1 year ago

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.

allenporter commented 1 year ago

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

DotNet2Web commented 1 year ago

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.

allenporter commented 1 year ago

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

DotNet2Web commented 1 year ago

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?

allenporter commented 1 year ago

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

DotNet2Web commented 1 year ago

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.

allenporter commented 1 year ago

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.

allenporter commented 1 year ago

RAINBIRD_PASSWORD is the environment variable needed for mitm to decode.

allenporter commented 1 year ago

We also have https://github.com/allenporter/pyrainbird/blob/main/CONTRIBUTING.md as an example

DotNet2Web commented 1 year ago

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.

allenporter commented 1 year ago

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

DotNet2Web commented 1 year ago

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

allenporter commented 1 year ago

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.

DotNet2Web commented 1 year ago

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 ?

allenporter commented 1 year ago

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)

DotNet2Web commented 1 year ago

StartedIrrigationRequest.txt StartedIrrigationResponse.txt

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

allenporter commented 1 year ago

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.

DotNet2Web commented 1 year ago

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

allenporter commented 1 year ago

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

salvoM commented 1 year ago

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?

allenporter commented 1 year ago

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