Simple, easy to use
After a few swipes in the app, we see the app works like this:
Ok, time to replicate this behavior myself...
For ease of implementation in I simply keep track of the last Sync ID in a flat file.
Lastly - Integrating with The Dash Button
Luckily, someone already did most of the hard work:
https://www.npmjs.com/package/dash-button
Piecing it All Together
Running The Daemon
Using Forever and an init.d script. Don't remember where I found it:
#!/bin/sh
#/etc/init.d/poopService
export PATH=$PATH:/usr/local/bin
export NODE_PATH=$NODE_PATH:/usr/local/lib/node_modules:/home/pi/node/dash/node_ modules
case "$1" in
start)
exec forever start --sourceDir=/home/pi/node/dash -p /home/pi/node/dash index.js
;;
stop)
exec forever stop --sourceDir=/home/pi/node/dash index.js
;;
*)
echo "Usage: /etc/init.d/poopService {start|stop}"
exit 1
;;
esac
exit 0
In Action!
But how can I take my smart phone out of the equation.
I have a few Amazon dash buttons I've been looking to do something with.
Now where do we start?
I have a few Amazon dash buttons I've been looking to do something with.
Now where do we start?
Let me inspect the calls being made by the app, and replicate that logic into my own library.
To inspect the calls, I used mitmproxy
After a few swipes in the app, we see the app works like this:
1. Login - which generates a session cookie
2. Get a snapshot of the devices in the "Sync Group."
3. Compare the Last Sync ID's for each device to determine if any changes have been made.
4. Get each new transaction and add those objects to the local app
5. POST any new transactions
Ok, time to replicate this behavior myself...
1. Login
First we need to generate our Own UUID for the "device" we wish to use. This UUID is static, as it's used to track the "device" adding the transactions.
POST https://prodapp.babytrackers.com/session
JSON:
{
"AppInfo": {
"AccountType": 0,
"AppType": 0
},
"Device": {
"DeviceName": "Unknown",
"DeviceOSInfo": "Nexus 5X OS 25",
"DeviceUUID": "12341234-3491-4fbc-a573-12341234" <-- Generate a UUID and need to
},
"EmailAddress": "XXXX@XXXX.com",
"Password": "XXXXXXX"
}
This returns a JSON Object with the AccountID - But it's not important.
However, we need to keep our cookie jar as it's the Session Cookie used for all future transactions.
2. Find other Devices in the sync group
GET https://prodapp.babytrackers.com/account/device
Response:
[
{
"DeviceUUID": "8f9e5727-4266-4c6a-98d1-111111111111",
"LastSyncID": 101
},
{
"DeviceUUID": "83a89be7-3491-4fbc-a573-111111111111",
"LastSyncID": 8
}
]
Since I'm just going to be injecting transactions I don't really need to maintain state and actually keep track of the SyncID's.
However, as we'll see in a little bit, every transaction entry must contain the "baby" Object.
The best way to build this object to include in our own transaction, is to retrieve it from the last transaction that was submitted.
3. Retrieve the latest Transaction - Store the "Baby" Object
We can get the transaction details by crafting a URL that includes the Device UUID and the Last Sync ID. We'll iterate through the last transaction from each device and make sure we're working with the latest baby object. (In case any changes were made)
GET https://prodapp.babytrackers.com/account/transaction/11111-D688-464C-1111111/16
Response:
[
{
"SyncID": 17,
"OPCode": 1,
"Transaction": "eyJkdWVEYXkiOiIyMDE3LTA0LTEyIDAwOjM2OjM4ICswMDAwIiwiQkNPYmplY3RUeXBlIjoiQmFieSIsImdlbasdfHNlIiwid
GltZXN0YW1wIjoiMjA23234123123AxNjozNzozMiArMDAwMCIsIm5hbWUiOiJKYW1lcyIsIm9iamVjdElEIjoiMDVhYWZmNTItOTVlMy00ZTNkLThmNDktNDY5NzJmMDdjNDNlIn0="
}
]
For whatever reason, the Actual transaction is Base64 Encoded. So if we decode we find:
{
{
"BCObjectType": "Diaper",
"amount": "2",
"status": "2",
"baby": {
"dob": "2017-04-06 20:32:33 +0000",
"dueDay": "2017-04-12 00:36:38 +0000",
"gender": true,
"name": "James",
"pictureName": "db309e71-4af9-4b5f-9786-123412341234",
"newFlage": false,
"objectID": "05aaff52-95e3-4e3d-8f49-123412341234",
"timestamp": "2017-04-10 14:02:17 +0000"
},
"note": "",
"pictureLoaded": true,
"pictureNote": [],
"time": "2017-04-12 16:00:13 +0000",
"newFlage": true,
"objectID": "4760f13c-ae88-4250-860b-123412341234",
"timestamp": "2017-04-12 16:18:24 +0000"
}
Awesome! Now we have the baby Object, that we can use in our future requests. AND we also know the data model for any Diaper changes!
4. Generate Our Own transaction.
Before we can submit our own requests, we need to make sure we have the right values for submission. The only value of note is "Status" which describes the type of Diaper: Wet, Dirty, or Mixed.
{
"BCObjectType": "Diaper",
"amount": "2",
"status": See Below,
"baby": { Previously Retrieved},
"note": "",
"pictureLoaded": true,
"pictureNote": [],
"time": "2017-04-12 16:00:13 +0000",
"newFlage": true,
"objectID": "Random UUID",
"timestamp": "2017-04-12 16:18:24 +0000"
}
status - Values are:
Wet - 0
Dirty - 1
Mixed - 2
6. Encapsulating the Event.
As we learned earlier, all Events are encoded as Base64 and wrapped in their own JSON message:
POST https://prodapp.babytrackers.com/account/transaction
{
"OPCode": "0",
"SyncID": 10,
"Transaction": "Base64 Encoded String"
}
Unfortunately, we can't be truly stateless. We need to be sure we're incrementing our own SyncID each time we submit a transaction. This ensures the app properly registers our transaction, and makes it available to other devices.
Unfortunately, we can't be truly stateless. We need to be sure we're incrementing our own SyncID each time we submit a transaction. This ensures the app properly registers our transaction, and makes it available to other devices.
For ease of implementation in I simply keep track of the last Sync ID in a flat file.
All of the above has nowl been captured in my Baby Helper Class
Luckily, someone already did most of the hard work:
https://www.npmjs.com/package/dash-button
Piecing it All Together
var pooBtn = new DashButton("68:54:fd:80:xx:xx");
var registerPoo = pooBtn.addListener(function(){
var timestamp = checkBtnPress();
if(timestamp)
{
console.log("Poo Occured At: " + timestamp);
babyHelper.logDiaper(1);
}
});
Sometimes it seems the Dash button will send multiple arp requests, resulting in multiple events firing. I handled this by implementing a time check, and ensuring only a single event can be registered inside 1 minute:
function checkBtnPress() {
var now = new Date();
var checkTime = new Date(lastEvent.setTime(lastEvent.getTime()+ 1000 * 60));
if(checkTime > now) {
//console.log("Duplicate Button Press");
return false;
} else {
var timestamp = dateFormat(now, "yyyy-mm-dd HH:MM:ss");
lastEvent.setTime(now.getTime());
return timestamp;
}
Running The Daemon
Using Forever and an init.d script. Don't remember where I found it:
#!/bin/sh
#/etc/init.d/poopService
export PATH=$PATH:/usr/local/bin
export NODE_PATH=$NODE_PATH:/usr/local/lib/node_modules:/home/pi/node/dash/node_ modules
case "$1" in
start)
exec forever start --sourceDir=/home/pi/node/dash -p /home/pi/node/dash index.js
;;
stop)
exec forever stop --sourceDir=/home/pi/node/dash index.js
;;
*)
echo "Usage: /etc/init.d/poopService {start|stop}"
exit 1
;;
esac
exit 0
In Action!
