Open malodie opened 3 months ago
On my iPad, I've had success with Proxyman. On Android I've had to use a mitm proxy to find the endpoints the app is hitting. I see a couple of fields in the existing LR4 endpoint for hopperStatus
with values like enabled, disabled, empty, but guessing when you click in there is more information than that.
Thanks for the response. I primarily work on windows at home (sad) but there is a proxyman windows app. If that drives me nuts I'll switch to ubuntu. I will play with it and see what I can find!
I tried to set up the proxy with my android phone (via my PC). It grabs the traffic but I can't actually see any of the requests fully, and it just seems to have a single endpoint that loads all of the data. I'm not seeing any of the direct API requests. I think it's having issues with the cert, which I set up following the instructions. I'll have to try either emulating it on my Windows PC, or come up with another idea, or at worst use some sort of proxy intercept on my phone.
Tell me this is going to be the one time I really wish I was in the mac ecosystem...
They explicitly state that this is harder with android.
I tried to set up the proxy with my android phone (via my PC). It grabs the traffic but I can't actually see any of the requests fully, and it just seems to have a single endpoint that loads all of the data. I'm not seeing any of the direct API requests. I think it's having issues with the cert, which I set up following the instructions. I'll have to try either emulating it on my Windows PC, or come up with another idea, or at worst use some sort of proxy intercept on my phone.
Tell me this is going to be the one time I really wish I was in the mac ecosystem...
They explicitly state that this is harder with android.
If you can share the full endpoint, I can check if I'm able to see a few things.
I only get so far as to see the api.onesignal.com and api.exponea.com in the list. But, it gives me an internal error. According to the troubleshooting, I can't actually get that information on Android because I do not own the application.
That might mean this is a nonstarter unless I can get my hands on an apple device.
From the docs:
If you're trying to intercept Android apps that you're not an owner -> It isn't possible to intercept -> ❌
https://docs.proxyman.io/troubleshooting/get-ssl-error-from-https-request-and-response#android-device
I'll check out Http Toolkit and see if I have more success.
You'll probably be looking for an iothings.site url as that is what most of the other endpoints use.
Yeah, you're right. I was able to at least see that URL with HTTP Toolkit, but there's still the android device issue.
I don't think there's a way for me to emulate this app.
On the bright side, my best friend has an iPad she's not using and has agreed to lend it to me, so I can hopefully move forward next week.
Thanks for your help on this :)
@malodie When I was doing some work on this to add pet profiles/weights I futzed around with a few options but ultimately settled on the following as it was easiest:
Had a quick poke and can confirm the method above works for the whisker app. Below are endpoints, requests and responses for:
Hope this helps. I don't actually have a hopper so can't really do much more unfortunately.
Endpoint: POST https://lr4.iothings.site/graphql
Request body:
{
"operationName": null,
"variables": {
"serial": "<REDACTED>",
"command": "disableHopper",
"commandSource": "app"
},
"query": "mutation sendLitterRobot4Command($serial: String!, $command: String!, $value: String, $commandSource: String) {\n __typename\n sendLitterRobot4Command(input: {serial: $serial, command: $command, value: $value, commandSource: $commandSource})\n}"
}
Response body:
{
"data": {
"__typename": "Mutation",
"sendLitterRobot4Command": "command \"disableHopper (0x020C0000)\" sent"
}
}
Endpoint: POST https://lr4.iothings.site/graphql
Request body:
{
"operationName": null,
"variables": {
"serial": "<REDACTED>",
"command": "enableHopper",
"commandSource": "app"
},
"query": "mutation sendLitterRobot4Command($serial: String!, $command: String!, $value: String, $commandSource: String) {\n __typename\n sendLitterRobot4Command(input: {serial: $serial, command: $command, value: $value, commandSource: $commandSource})\n}"
}
Response body:
{
"data": {
"__typename": "Mutation",
"sendLitterRobot4Command": "command \"enableHopper (0x020C0001)\" sent"
}
}
Endpoint: POST https://lr4.iothings.site/graphql
Request body:
{
"variables": {
"serial": "<REDACTED>",
"consumer": "app",
"limit": 10,
"activityTypes": [
"litterHopperDispensed"
]
},
"query": " query GetLR4($serial: String!, $consumer: String, $startTimestamp: String, $endTimestamp: String, $limit: Int, $activityTypes: [String]) {\n getLitterRobot4Activity(serial: $serial, consumer: $consumer, startTimestamp: $startTimestamp, endTimestamp: $endTimestamp, limit: $limit, activityTypes: $activityTypes) {\n value\n timestamp\n measure\n actionValue\n }\n }\n "
}
Response body:
{
"data": {
"getLitterRobot4Activity": []
}
}
Also got this over the websocket wss://lr4.iothings.site/graphql/realtime?header=<REDACTED - JWT Token>&payload=e30%3D
{
"id": "<REDACTED - uuid v4 id string>",
"type": "start",
"payload": {
"data": "{\"variables\":{\"userId\":\"<REDACTED - integer>\"},\"query\":\" subscription GetLR4($userId: String!) {\\n litterRobot4StateSubscriptionByUser(userId: $userId) {\\n robots {\\n name\\n serial\\n unitId\\n unitPowerType\\n unitPowerStatus\\n robotStatus\\n unitTimezone\\n unitPowerStatus\\n isOnboarded\\n setupDateTime\\n cleanCycleWaitTime\\n isKeypadLockout\\n nightLightBrightness\\n nightLightMode\\n litterLevel\\n DFILevelPercent\\n globeMotorFaultStatus\\n catWeight\\n isBonnetRemoved\\n isDFIFull\\n wifiRssi\\n isOnline\\n espFirmware\\n picFirmwareVersion\\n laserBoardFirmwareVersion\\n isFirmwareUpdateTriggered\\n sleepStatus\\n catDetect\\n robotCycleState\\n robotCycleStatus\\n isLaserDirty\\n panelBrightnessHigh\\n smartWeightEnabled\\n surfaceType\\n hopperStatus\\n scoopsSavedCount\\n isHopperRemoved\\n litterLevelPercentage\\n litterLevelState\\n weekdaySleepModeEnabled {\\n Sunday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Monday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Tuesday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Wednesday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Thursday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Friday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n Saturday {\\n sleepTime\\n wakeTime\\n isEnabled\\n }\\n }\\n }\\n }\\n }\\n \"}",
"extensions": {
"authorization": {
"Accept": "application/json, text/javascript",
"Content-Encoding": "amz-1.0",
"Content-Type": "application/json; charset=utf-8",
"Authorization": "Bearer <REDACTED - JWT token>",
"Host": "lr4.iothings.site"
}
}
}
}
Thanks for those @jrhe. I saw the hopperStatus
and isHopperRemoved
fields before, but wasn't sure what would come back in hopperStatus
. Initially I thought there might be a different endpoint to get more details about the hopper accessory, but looking at the information here and on Whisker's website, I don't think there is much more info about the hopper than these couple of fields.
I think the API just provides those two vars and then allows you to query dispensing history (litterHopperDispensed
activity type).
hopperStatus
should be something like full/running low/empty based on the screenshots I have seen.
I think there is a distinction between the hopper being enabled, and hopper being removed. When I tried to enable the hopper without actually having one installed I got a "LitterHopper Error: Motor fault di... - There's an issue with the LitterHopper's motor connection. [...]".
The hopper itself seems to just have a single barrel jack, that presumably provides power, with no additional sensors. I think any data provided is calculated based on the resistance of the motor (missing, spinning, spinning freely/empty), the LR's litter level sensors, and how many times the hopper has run since being refilled.
Hopefully @malodie can check out the exact values!
Unfortunately I still don't have an iPad to check this :(
You should be able to install the android emulator as above if you want to do it without an ipad
@natekspencer @malodie Just checking to see if this additional functionality is coming. For me, I'd love to turn off the hopper at night because its a bit noisier than the LR4 itself.
Let me know if I can help with testing, etc.
I tried to do it with android and I can't capture the traffic. Sorry. If I get an ipad I will but I have no idea when that will be as I own no apple products.
LitterRobot has added a new feature to the LR4, the litter hopper. It would be excellent to add this as a feature in pylitterbot to then extend that to Home Assistant. The app does not display the litter hopper information until you physically click into the Litter Robot section and the hopper itself.
I'm hoping to contribute and add this feature, but I am unsure of how to use the non-public API to see if this information is available. Due to its availability on the app, I am assuming it is available in the API. I will need to spend some time discovering how this works, or if you have any shortcuts available (e.g. a way to list endpoints) or some useful documentation around accessing and reading non-public graphql that would be really helpful.