Litter Robot Connect

Is it possible for smartthings to integrate with new litter robot connect?
https://www.litter-robot.com/accessories/litter-robot-iii-connect-upgrade-kit.html#toggle-id-3

3 Likes

Looks like the app works over WiFi and has no Zigbee or Z-Wave connectivity. I am pretty sure it would have to be written and/or run through a 3rd party site.

Thanks for the heads up. I went ahead and bought the upgrade kit. (We love our Litter-Robot.) Looks like I’ll be waiting on the Android App release, but I wanted to get the reduced Black Friday price on the kit. I’m guessing any ST integration will have to be cloud to cloud if an API is ever exposed. We might at least see some official Alexa integration initiated by the Litter Robot co. in the future.

I just looked at it. Looks like $500 for a litter box…a little steep for stool. What does the app do?

It steep but for a 2 cat household where no one likes to scoop litter it is great. The new app functionality looks like it gives you remote access and alerts when it need to be emptied

It looks like it keeps a history as well. That might come in handy for noticing a behavioral or health issue. I’m guessing the weight sensor in the robot doesn’t actually measure the weight of the cat… too bad.

I bought the connect and it should be delivered by the time I get home today. I plan to try and reverse engineer their API and if I get some free time I’ll put together a device handler for it. I’m gonna connect the robot to a hotspot on my pc and see what wireshark finds. They don’t support android yet, so I bought a Iphone 4S off ebay for $50 so that I could do the initial setup.

I did shoot them an email asking if they would just give me the endpoint information but they said that the information wasn’t available publicly just yet, but they are working on putting it together for early 2018.

I’ll post whatever I find here.

I hope to eventually set up something to annoy my wife whenever the litter robot is full, as she sometimes forgets to check it when I’m out of town. :smiley:

1 Like

temporarily removed information to double-check some things.

There is also some POST/GET traffic from the app to the following hosts:

onesignal.com — appears to be a push notification api provider
api.mixpanel.com — They do in-app tracking/analytics to see how people are using the app, what icons are pressed, etc.

1 Like

Following this! I’m not lazy enough that i have to just push a button on my phone to cycle my litter box, I want to tell Alexa to do it! The FAQ on their site now says the below as Collisionc stated, so that’s positive.

Q: Is the Litter-Robot compatible with IFTTT, MQTT, and other third-party home automation systems?

A: While we do have plans to make the Litter-Robot Connect API available to the public, it is not at this time. We plan to open the API in 2018.

For anybody else looking for information about the protocol, here’s what I’ve found:

Once the litter robot is set up (using the iOS app), it communicates via UDP from port 2001 to a server running at dispatch.iothings.site on port 2000.

Outgoing (Litter Robot to Server)

The protocol is pretty simple and consists of the Litter Robot device sending periodic stats updates to the server like so:

>LR3,ff999aaafff000,H,AC,DFS,W7,NL1,SM0,PL0,CS0100,38FA,8A23DAFE

This breaks down to:

  • > - “outgoing message from Litter Robot to server”
  • LR3 - Model number (Litter Robot 3?)
  • ff999aaafff000 - Unique ID for the registered device on the dispatch.iothings.site server
  • H - Not sure. I’ve only ever seen “H”.
  • AC - Power mode (AC / ??? for battery). Never seen anything other than AC in my installation.
  • Rdy - State (see below for known state codes)
  • W7 - Wait time before cycling, in minutes (e.g. W7, W3, W10?)
  • NL1 - Nightlight state (1 = on, 0 = off)
  • SM0 - Sleep mode off - when on, the code is more complicated, like SM123:34:01. I haven’t tried to figure this out entirely, since I don’t use this.
  • PL0 - Panel lock state (1 = locks, 0 = unlocked)
  • CS0100 - not sure - this always starts with CS and has ranged from CS00CB to CS33FF in my current logged messages
  • 38FA - Message number - increments every message. Resets to 0 when the unit is power cycled.
  • 8A23DAFE - I think this is a checksum. I haven’t tried hard to get a consistent message (including message number) to see if this is entirely dependent on the message being sent.

Possible status codes and my best interpretation from memory:

  • CCC - Cat cycle completed
  • CCP - Cat cycle processing (spinning)
  • CSF - Cat sensor full (poop dump time)
  • CSI - Cat sensor interrupted (cat got too curious mid-cycle)
  • CST - Cat sensor triggered (special present deposited, ready for processing)
  • DF1 - Drawer is Full - Can cycle 2 more times
  • DF2 - Drawer is Full - Can cycle 1 more time
  • DFS - Drawer is full - Will not cycle anymore
  • BR - Bonnet removed
  • P - Unit is Paused
  • OFF - Unit is turned off
  • Rdy - Ready for cats

(thanks, @Collisionc for the extra status codes interpretation)

On success, the server responds with a simple:

AOK,ff999aaafff000

I don’t care much about fooling the server (or even care about the server until they have Android support), but I haven’t been able to spoof a message, since I don’t know how the checksum is generated.

Incoming (Server to Litter Robot)

The server has a variety of control signals it can send. For example:

<C,LR3,ff999aaafff000,06EB,7AE2E42F

  • < - “incoming message from server to Litter Robot”
  • LR3 - Model number (Litter Robot 3?)
  • ff999aaafff000 - Target device ID
  • 06EB - Message counter (does not correspond to last outgoing message from Litter Robot)
  • 7AAAEEEF - I think this is a checksum. No idea how to generate this, and if it’s not right, the Litter Robot rejects the message.

From @Collisionc’s post, below:

Here are the incoming (server to litter robot) commands:

  • <C == Start cleaning cycle
  • <W7 == Set wait time to 7 minutes
  • <W3 == Set wait time to 3 minutes
  • <WF == Set wait time to 15 minutes
  • <P0 == Turn off
  • <P1 == Turn on
  • <N1 == Turn on night light
  • <N0 == Turn off night light
  • <S0 == Turn off sleep mode
  • <S119:45:02 == Turn on sleep mode, set to start at 19:45:02 MST – Kind of a guess here, it says 10 PM on the app when I use this, and I’m EST. I think it starts 15 minutes to prevent a cycle from happening after a 15 minute wait.
  • <L1 == Turn on panel lock
  • <L0 == Turn off panel lock

Practical application

I’ve set up my router to redirect (outgoing NAT) all connections from the litter box to the server and send to a script I’m running on an internal computer. This allows me to intercept and relay all communications out to the Litter Robot server, and to route returned messages back to the Litter Robot. I have an EdgeRouter and I did this through the UI, but it could also be done with iptables similar to (the following may not work exactly, it was from my initial testing phase:

iptables -t nat -I PREROUTING -i eth1 -p tcp --dport 2000 -j DNAT --to 10.10.11.21

Doing this allows me to do whatever I want with the status of the litter box (which is, honestly, all I really care about). From here, it’s only necessary to write the necessary code for SmartThings and bridge the interceptor with the SmartThings device code. I’m not horribly familiar with SmartThings, but I’ll work with someone if they want to do that part.

Note

All of the IDs and checksums above are mocked. I don’t really want to publish my device’s ID, but I’m happy to work directly with someone who is more familiar with tracking down how to reverse engineer a checksum algorithm.

The only thing this would give you is the ability to control the litter box - which is of limited use to me. You can cycle the litter box, change settings, etc, but I can’t think of a real use for that as a remote function.

3 Likes

Any progress here? I’d rather just poll the HTTP endpoint you discovered than snoop the UDP traffic (although that’s been working reliably so far).

As the owner of cats myself and an advocate of home automation, some things just shouldn’t be automated/integrated. Just because you can, doesn’t mean you should.

I have owned 4 of the Pet Safe self cleaning litter boxes for 2 cats for the last 3 years and not a single issue with any of them. They are automated amongst themselves in that they have a timer that sifts the litter at 5, 10 and 20 minutes. They have a counter on them and a visual when the lid begins to rise as to when to change out the box, which I change out the litter trays once every 30 days on average and takes about 10 minutes a piece. That’s about as hassle free as you are going to get when it comes to the task of cats and litter boxes. 4 of these cost $600 together compared to one of these. $350 for Wifi? :joy:

If the spouse has trouble checking from time to time to check the litter boxes (every couple of days) on her own (pet responsibility), and creating some sort of automation to notify or as it was put “something to annoy her”, ya think that’s going to solve the issue of being responsible. That’s like saying we have two kids and when I’m out of town, she forgets to check diapers and change them, so let me attach a leak detector to the diaper and send notifications to her, just to remnind (annoy) her. Any difference between the caring of pets and kids, I think not.

Now if you had the ability to perform a biopsy on the waste, take vitals and collect weight information, then maybe from a health perspective, I might agree that having some sort of automation to collect and store this data might be worthwhile. While you are at it, be as invasive as you can and attach a camera inside each litter box to see who is doing #1 and #2. :thinking:

Not to mention the safety issues that potentially could come of this by allowing buttons to be pushed remotely and causing unknown harm to your cats (like automating the closing of a garage door when you have no visual of the door itself) that inadvertantly begins sifting and your cat gets stuck or breaks a leg, or worse.

Sorry just my .02 on why some things around technology should just be left alone. Good luck with the integration though.

Merry Christmas!

As an owner of multiple cats and one of these robots:

No safety issue is introduced. These litter robots won’t cycle when there is a cat in the unit.

In my opinion, the connectivity benefit is primarily informational. You’ll know the frequency of use and can potentially see if something is amiss. Push notification is great for a system that is encouraging you to be hands off. (What’s the point of an automated system if someone has to monitor it routinely in case the hidden receptical is near capacity or full.)

For the record, I wouldn’t automate the care of an infant like I would the waste processing for a feline. Forgetting to regularly check a disposal unit for a flashing light is not akin to forgetting to check on a baby. And I only wish this thing came with a camera so I could know which one of the cats has decided to opt out.

With ST integration, some people may want to trigger an exhaust fan after each use or push notify after x number of cycles to remind them to completely replace the litter or clean the unit. (To name just a few off the top of my head.)

I was able to reverse engineer the API, but in order for it to work I would need to post the apikey that the Iphone app uses. The only way to get that key is via a mitm proxy where you force your iphone to connect to a proxy that intercepts the traffic.

I emailed the company that makes the litter robot asking if it was ok to post information about the http endpoints, apikey, etc, and they asked me to wait on it as they were going to make changes to the http API soon. I’ll honor this for the short term, but if they don’t have anything released by Feb/Mar I’ll post it all.

Here are the additional status codes I have found:

DF1 == Drawer is Full - Can cycle 2 more times
DF2 == Drawer is Full - Can cycle 1 more time
DFS == Drawer is full - Will not cycle anymore
BR == Bonnet removed
P == Unit is Paused
OFF == Unit is turned off

Here are the incoming (server to litter robot) commands

  • <C == Start cleaning cycle
  • <W7 == Set wait time to 7 minutes
  • <W3 == Set wait time to 3 minutes
  • <WF == Set wait time to 15 minutes
  • <P0 == Turn off
  • <P1 == Turn on
  • <N1 == Turn on night light
  • <N0 == Turn off night light
  • <S0 == Turn off sleep mode
  • <S119:45:02 == Turn on sleep mode, set to start at 19:45:02 MST – Kind of a guess here, it says 10 PM on the app when I use this, and I’m EST. I think it starts 15 minutes to prevent a cycle from happening after a 15 minute wait.
  • <L1 == Turn on panel lock
  • <L0 == Turn off panel lock
1 Like

Hey guys, love your work. I’ve been doing a bit of this myself for my own purposes already and stumbled across this thread and thought I’d share.

This is purpose built to ingest the data into a data platform called Splunk, but you can use the code straight out of the bin/ directory.

1 Like

Nice, I guess since someone else posted information, no reason in holding back now.

edit: currently this will only function if you have onboarded your litter robot on the iphone app, I need to look back over my mitm logs and see what was done to onboard the robot at first. This will also not properly work with multiple litter robots on 1 account, but I’ll make adjustments to fix that in the future.

Currently, I have a python script running to fit my purposes on my multi-purpose linux box. All you have to do is edit the email/password lines and fill in your litter robot account information. You can create an account on their website, no need to use the app.

Basically, this script will check if it has the authentication token first, grab it if needed, then check the current status of the robot. I currently have it setup to activate webcore pistons if certain statuses are seen, such as any error code or if the device is full.

I run the script every minute via cron, using this line:

* * * * * /usr/bin/python3 /opt/cat.py

Edit /opt/cat.py to wherever you put it.

import logging
import requests
import json
import linecache

email = 'youremail@address.com'
password = 'yourpassword'
logs_enabled = True
log_location = '/opt/cat.log'

auth_token = None
userid = None

auth_token = linecache.getline('litter_robot_auth.txt',1).rstrip()
user_id = linecache.getline('litter_robot_auth.txt',2).rstrip()
if auth_token is '' or user_id is '':
    r = requests.post('https://muvnkjeut7.execute-api.us-east-1.amazonaws.com/staging/login', json={'email':email,"oneSignalPlayerId":"0","password":password}, headers={'x-api-key':'Gmdfw5Cq3F3Mk6xvvO0inHATJeoDv6C3KfwfOuh0'})
    response_native = json.loads(r.text)

    if 'token' in response_native:
        token = response_native['token']
    if 'user' in response_native:
        userid = response_native['user']['userId']

    if token is not None and userid is not None:
        file = open('litter_robot_auth.txt','w')
        file.write( token + '\n' )
        file.write( userid )
        file.close()

auth_token = linecache.getline('litter_robot_auth.txt',1).rstrip()
user_id = linecache.getline('litter_robot_auth.txt',2).rstrip()

r = requests.get('https://muvnkjeut7.execute-api.us-east-1.amazonaws.com/staging/users/' + user_id + '/litter-robots', headers={'x-api-key':'Gmdfw5Cq3F3Mk6xvvO0inHATJeoDv6C3KfwfOuh0','Authorization':auth_token})

response_native = json.loads(r.text)

if logs_enabled is True:
    logging.basicConfig(format='%(asctime)s %(message)s',filename=log_location,level=logging.WARNING)
if 'unitStatus' in response_native[0]:
    status = response_native[0]['unitStatus']
    if logs_enabled is True:
        logging.warning('%s' % status )
    if status == 'DF1' or status == 'DFS' or status == 'DF2':
        new = requests.get('%WEBCORE PISTON TO TURN ON RED LIGHT%')
    elif status == 'RDY':
        new = requests.get('%WEBCORE PISTON TO SET LIGHT TO DEFAULT%')
    elif status == 'BR' or status == 'CSF' or status == 'PDF' or status == 'OTF' or status == 'PF':
        new = requests.get('%WEBCORE PISTON TO TURN ON RED LIGHTS + FLASH%')

The webcore pistons are just pistons that I execute via http requests.

I haven’t had time to put together a device handler, which would allow for more flexibility as the only thing I cared about was if the litterbox needed to be emptied. I’ll post all the http endpoints sometime this week, and someone else should be able to create a device handler/smartapp if they so wish.

4 Likes

I can’t find a method of onboarding the litter robot without the iphone app at the moment. Once the robot has been linked to your account via the iphone app, my script will work fine. The problem lies in connecting it to local wifi for the first time, temporarily connecting to the litter robot’s SSID and then giving it the password for the home wifi.

Here are the http endpoints I found:

POST https://muvnkjeut7.execute-api.us-east-1.amazonaws.com/staging/users/(userid)/litter-robots/(litterRobotId)/dispatch-commands
    request
    {
        "command": "<C",
        "litterRobotId": "redact"
    }
    response
    {
        "_created": "2017-12-04T23:00:45.705702",
        "_developerMessage": "Command: <C posted to litterRobotId: redact",
        "_requestId": "redact",
        "_uri": "/users/(userid)/litter-robots/(litterRobotId)/dispatch-commands",
        "command": {
            "command": "<C",
            "litterRobotId": "redact",
            "timestamp": "2017-12-04T23:00:45.705702"
        }
    }

    so far the commands I have captured:
    "<C" == Start cleaning cycle
    "<W7" == Set wait time to 7 minutes
    "<W3" == Set wait time to 3 minutes
    "<WF" == Set wait time to 15 minutes
    "<P0" == Turn off
    "<P1" == Turn on
    "<N1" == Turn on night light
    "<N0" == Turn off night light
    "<S0" == Turn off sleep mode
    "<S119:45:02" == Turn on sleep mode, set to start at 19:45:02 MST  -- Kind of a guess here, it says 10 PM on the app when I use this, and I'm EST. I think it starts 15 minutes to prevent a cycle from happening after a 15 minute wait.
    "<L1" == Turn on panel lock
    "<L0" == Turn off panel lock


    isDFITriggered can either be 0 or 1, 1 meaning that the drawer is full.

    unitStatus has the following codes (so far)
    "RDY" == Unit ready to be used.
    "CCP" == Cleaning Cycle in Progress
    "CCC" == Cleaning Cycle Completed
    "DF1" == Drawer is Full -- But it is able to cycle a few more times.
    "CSI" == Cat sensor interrupt
    "BR" == Bonnet removed
    "P" == Unit is Paused
    "OFF" == Unit is turned off
    "SDF" == Drawer is completely full and will not cycle. 


    PATCH https://muvnkjeut7.execute-api.us-east-1.amazonaws.com/staging/users/(userid)/litter-robots/(robotid)
    PATCH http commands have the same header as get

    PATCH is used to change the name of the robot and nothing else (that I can tell)
    For this one, I edited the name of the robot. 
    Request:
    {
        "cycleCapacity": "36",
        "cycleCount": "0",
        "cyclesAfterDrawerFull": "0",
        "litterRobotNickname": "NewName",
        "litterRobotSerial": "redact"
    }


GET required parts of header:
Host:	muvnkjeut7.execute-api.us-east-1.amazonaws.com
x-api-key: Gmdfw5Cq3F3Mk6xvvO0inHATJeoDv6C3KfwfOuh0
Authorization: token that is given upon login, unique to the account.

    GET /staging/users/(userid)/litter-robots/(robotid)
        [
            {
                "DFICycleCount": "0",
                "cleanCycleWaitTimeMinutes": "7",
                "cycleCapacity": "36",
                "cycleCount": "5",
                "cyclesAfterDrawerFull": "0",
                "isDFITriggered": "0",
                "isOnboarded": true,
                "lastSeen": "2017-12-02T06:09:00.306186",
                "litterRobotId": "redact",
                "litterRobotNickname": "redact",
                "litterRobotSerial": "redact",
                "nightLightActive": "1",
                "panelLockActive": "0",
                "powerStatus": "AC",
                "setupDate": "redact",
                "sleepModeActive": "0",
                "sleepModeEndTime": "0",
                "sleepModeStartTime": "0",
                "unitStatus": "RDY"
            }
        ]

    GET staging/users/(userid)
            {
                "litterRobots": [],
                "mobileDevices": [
                    {
                        "deviceId": "redact",
                        "oneSignalPlayerId": "redact",
                        "userId": "redact"
                    }
                ],
                "settings": {
                    "BR_notifications": 1,
                    "CCC_notifications": 0,
                    "CSF_notifications": 1,
                    "CSI_notifications": 0,
                    "DFI_notifications": 1,
                    "DHF_notifications": 1,
                    "OTF_notifications": 1,
                    "PD_notifications": 1,
                    "all_notifications": 1,
                    "cycleNotifications": "0",
                    "fault_notifications": "1",
                    "general_notifications": "1",
                    "userId": "redact"
                },
                "user": {
                    "firstName": "redact",
                    "lastName": "redact",
                    "userEmail": "redact",
                    "userId": "redact"
                }
            }

GET /staging/users/(userid)/litter-robots/(robotid)/insights?days=7&timezoneOffset=-0500
Reponse:
{
    "averageCycles": 5.0,
    "cycleHistory": [
        {
            "cyclesCompleted": 0,
            "date": "2017-12-02"
        },
        {
            "cyclesCompleted": 5,
            "date": "2017-12-01"
        },
        {
            "cyclesCompleted": 0,
            "date": "2017-11-30"
        },
        {
            "cyclesCompleted": 0,
            "date": "2017-11-29"
        },
        {
            "cyclesCompleted": 0,
            "date": "2017-11-28"
        },
        {
            "cyclesCompleted": 0,
            "date": "2017-11-27"
        },
        {
            "cyclesCompleted": 0,
            "date": "2017-11-26"
        }
    ],
    "totalCycles": 5
}


GET /staging/users/(userid)/litter-robots/(robotid)/activity?limit=200
Response:
{
    "activities": [
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T06:12:59.780813",
            "unitStatus": "RDY"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:25:19.020237",
            "unitStatus": "CCC"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:24:58.620250",
            "unitStatus": "CCP"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:22:42.726967",
            "unitStatus": "CST"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:15:18.991654",
            "unitStatus": "RDY"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:15:12.840294",
            "unitStatus": "CCC"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:14:51.781562",
            "unitStatus": "CCP"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:12:33.474309",
            "unitStatus": "CST"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T04:04:21.660154",
            "unitStatus": "RDY"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:42:06.938044",
            "unitStatus": "CCC"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:41:45.235959",
            "unitStatus": "CCP"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:39:08.926714",
            "unitStatus": "CST"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:30:02.105166",
            "unitStatus": "RDY"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:25:47.510691",
            "unitStatus": "CCC"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:25:26.461123",
            "unitStatus": "CCP"
        },
        {
            "litterRobotId": "redact",
            "timestamp": "2017-12-02T03:22:55.126963",
       }
]
}

POST Header:

Host:	muvnkjeut7.execute-api.us-east-1.amazonaws.com
x-api-key:	Gmdfw5Cq3F3Mk6xvvO0inHATJeoDv6C3KfwfOuh0
Authorization: token that is given upon login, unique to the account, only necessary for sending commands.
POST /staging/login
Request:
{
    "email": "redact",
    "oneSignalPlayerId": "redact",
    "password": "redact"
}
Response:
{
    "_created": "redact",
    "_developerMessage": "Login successful",
    "_requestId": "redact",
    "_uri": "/users/(userid)",
    "identityId": "redact",
    "status": "200",
    "token": "redact",
    "user": {
        "firstName": "redact",
        "lastName": "redact",
        "userEmail": "redact",
        "userId": "redact"
    }
}


POST /staging/users/(userid)/logout
Request: 
{
    "oneSignalPlayerId": "redact"
}
Response:
{
    "_created": "redact",
    "_developerMessage": "Logged out user redact",
    "_requestId": "redact",
    "_uri": "/users/(userid)/logout"
}
3 Likes

Just as a heads up, just got an email (probably everyone did) that they are making large changes to the app that will make the old version not work at all. No clue what they’re changing so this script may not work in a few days. I’ll update it if I can.

1 Like

Doesn’t seem like any behind the scenes changes that I can tell. They might have changed how multiple litter robots show up, but I don’t have enough cats to justify another one of those :stuck_out_tongue:

© 2019 SmartThings, Inc. All Rights Reserved. Terms of Use | Privacy Policy

SmartThings; SmartApps®; Physical Graph; Hello, Home; and Hello, Smart Home are all trademarks of the SmartThings, Inc.