nblavoie/wyzecam-api

I made progress

Opened this issue · 8 comments

I was able to use Fiddler to sniff traffic coming from the Wyze app and was able to find all the API endpoints to login, get a device list with status, change the state of a device and logout. I haven't worked out the API to refresh the access token so I am logging in/out each time to get a brand new token so it's not super efficient. I was also unable to figure out the password hash. MD5(MD5(plaintextpassword)) isn't working on the API endpoints I am using. I had use Fiddler to find my password hash and phone id. Anyways, here is some sample code:

const request = require('request')
const md5 = require('md5')
const uuid = require('uuid').v1

var email = 'joeb@example.com' // your wyze username
var password = 'password' // your plain text wyze password

var phone_id = uuid() // Generate random uuid to use for phone_id
var api_key = 'RckMFKbsds5p6QY3COEXc2ABwNTYY0q18ziEiSEm'
var passwordHash = md5(md5(md5(password)))

function login(phone_id, email, passwordHash, loginCb) {
    request({
        url: 'https://auth-prod.api.wyze.com/user/login',
        method: 'POST',
        headers: {
            "user-agent": "wyze_android_2.11.40",
            "x-api-key": api_key,
            "phone-id": phone_id,
            "content-type": "application/json; charset\u003dutf-8",
        },
        json: { email: email, password: passwordHash }
    }, (err, res, json) => {
        loginCb(json.access_token)
    })
}

function getDevices(access_token, phone_id, getDevicesCb) {
    request({
        url: 'https://api.wyzecam.com/app/v2/home_page/get_object_list',
        method: 'POST',
        headers: {
            "user-agent": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Fire Build/LMY49M)",
            "content-type": "application/json",
        },
        json: { "access_token": access_token, "app_name": "com.hualai", "app_ver": "com.hualai___2.11.40", "app_version": "2.11.40", "phone_id": phone_id, "phone_system_type": "2", "sc": "9f275790cab94a72bd206c8876429f3c", "sv": "9d74946e652647e9b6c9d59326aef104", "ts": Date.now() }
    }, (err, res, body) => {
        getDevicesCb(body.data.device_list)
    })
}

function logout(access_token, phone_id, logoutCb) {
    request({
        url: 'https://api.wyzecam.com/app/user/logout',
        method: 'POST',
        headers: {
            "user-agent": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Fire Build/LMY49M)",
            "content-type": "application/json",
        },
        json: { "sc": "a626948714654991afd3c0dbd7cdb901", "sv": "759245b61abd49128585e95f30e61add", "app_ver": "com.hualai___2.11.40", "ts": Date.now(), "access_token": access_token, "phone_id": phone_id, "app_name": "com.hualai", "app_version": "2.11.40" }
    }, (err, res, body) => {
        logoutCb()
    })
}

function runAction(access_token, phone_id, instance_id, action_key, runActionCb) {
    request({
        url: 'https://api.wyzecam.com/app/v2/auto/run_action',
        method: 'POST',
        headers: {
            "user-agent": "Dalvik/2.1.0 (Linux; U; Android 5.1.1; Fire Build/LMY49M)",
            "content-type": "application/json",
        },
        json: { "sc": "a626948714654991afd3c0dbd7cdb901", "sv": "011a6b42d80a4f32b4cc24bb721c9c96", "app_ver": "com.hualai___2.11.40", "ts": Date.now(), "access_token": access_token, "phone_id": phone_id, "app_name": "com.hualai", "app_version": "2.11.40", "provider_key": "WLPP1", "action_key": action_key, "instance_id": instance_id, "action_params": {}, "custom_string": "" }
    }, (err, res, body) => {
        runActionCb()
    })
}

// Login; list devices; find the device we want to control; toggle power state; logoff
var nickname = "Bedroom Lamp"
login(phone_id, email, passwordHash, access_token => {
    getDevices(access_token, phone_id, devices => {
        devices.forEach(device => {
            if (device.nickname == nickname) {
                console.log(`${nickname} is ${(device.device_params.switch_state == 0 ? 'off' : 'on')}!`)
                runAction(access_token, phone_id, device.mac, (device.device_params.switch_state == 0 ? 'power_on' : 'power_off'), () => {
                    console.log(`Turning ${nickname} ${(device.device_params.switch_state == 1 ? 'off' : 'on')}!`)
                    logout(access_token, phone_id, () => { })
                })
            }
        })
    })
})

This code is written in Nodejs and depends on request, md5 and uuid (npm i request md5 uuid).

I figured out the new password hash. Rather than md5(md5(password)) it is now md5(md5(md5(password))).

I updated the sample code above to use the new hash. Also I noticed the phone_id can be any randomly generated guid. So this sample code will work without having to sniff any of your own data.

Nice work!

@nblavoie: It is possible to move this repository?
You have stopped the development, no?

Maybe @noelhibbard can now manage it, no?

Great working example @noelhibbard,
By any chance do you know what would be the code to enable/disable motion detection on the Wyzecam?
I currently use IFTTT to control my Wyzecam with but I'd rather to independently connect via an API service like this one.

Many thanks

Great working example @noelhibbard,
By any chance do you know what would be the code to enable/disable motion detection on the Wyzecam?
I currently use IFTTT to control my Wyzecam with but I'd rather to independently connect via an API service like this one.

Many thanks

Sorry, I don't have any cameras. I had one but bricked it while trying to flash a 3rd party firmware. You should take a look at this project:
https://github.com/misenhower/homebridge-wyze-connected-home

It's a homebridge plugin that seems to cover most of the Wyze accessories. You could probably find hints at how you could accomplish what you need.

So I created https://github.com/noelportugal/wyze-node a while ago...I'm trying to revisit and remove the need to find the phoneId...I wonder why would it work for you using node package uuid and not for me? I'm not using api_key so I wonder if that is the reason?

So I created https://github.com/noelportugal/wyze-node a while ago...I'm trying to revisit and remove the need to find the phoneId...I wonder why would it work for you using node package uuid and not for me? I'm not using api_key so I wonder if that is the reason?

I'm not really sure. I basically just packet sniffed the whole process when running the iOS Wyze app and then reproduced it in Node. Or maybe Wyze wants a v1 UUID (because it contains a timestamp).

Yeah I tried UUID v1 and v4 with no luck...I gave up and hardcoded the phoneid for now...Wyze doesn't seem to care 🤷.