Cosori Dual Blaze 6.8-Quart Smart Air Fryer CAF-P583S
Discussed in https://github.com/webdjoe/pyvesync/discussions/249
Originally posted by webdjoe August 6, 2024 Discussion for Cosori Dual Blaze 6.8-Quart Smart Air Fryer CAF-P583S
#55
https://github.com/webdjoe/pyvesync/issues/55#issuecomment-1781441132
POST /cloud/v2/deviceManaged/configurationsV2 HTTP/2
{
"traceId": "1698336540120",
"code": 0,
"msg": "request success",
"module": null,
"stacktrace": null,
"result": {
"ownerShip": true,
"hygrometerInfo": null,
"switchInfo": null,
"macID": "4c:75:25:11:11:11",
"btMacID": "4c:75:25:11:11:11",
"airPurifierInfo": null,
"deviceRegion": "US",
"deviceName": "Air Fryer",
"deviceImg": "https://image.vesync.com/defaultImages/deviceDefaultImages/wfon_afr_caf-p583s-kus_us_240.png",
"uuid": "47012820-3541-48e0-b4d4-1111111",
"connectionType": "WiFi+BTOnboarding+BTNotify",
"deviceStatus": "off",
"ovenInfo": null,
"configModule": "WFON_AFR_CAF-P583S-KUS_US",
"meatThermometerInfo": null,
"sousVideCookerInfo": null,
"wifiName": "Down_IoT",
"subDevices": null,
"latestFirmVersion": "1.0.15",
"deviceProp": null,
"airFryerInfo": {
"tempUnit": 1,
"workTempUnit": "f"
},
"deviceType": "CAF-P583S-KUS",
"rssi": -49,
"allowNotify": "on",
"wifiMacID": "4c:75:25:11:11:11",
"firmwareUrl": "http://fw.vesync.com/WFON_AFR_CAF-P583S-KUS_US/mcuFirmware/v1.0.15/CAF-P583S-KUS_MCU_C1_ota_v1.0.15.bin",
"currentFirmVersion": "1.0.15",
"connectionStatus": "online",
"defaultDeviceImg": "https://image.vesync.com/defaultImages/deviceDefaultImages/wfon_afr_caf-p583s-kus_us_240.png",
"humidifierInfo": null,
"outletInfo": null,
"cid": "vsskf5a9079c41eca7592f65c4111111"
}
}
-----------
POST /cloud/v2/deviceManaged/bypassV2 HTTP/2
{
"traceId": "1698336538629",
"code": 0,
"msg": "request success",
"module": null,
"stacktrace": null,
"result": {
"traceId": "1698336538629",
"code": 0,
"result": {
"stepArray": [
{
"cookSetTime": 60,
"cookTemp": 175,
"mode": "AirFry",
"cookLastTime": 60,
"shakeTime": 0,
"cookEndTime": 0,
"recipeName": "Air Fry",
"recipeId": 14,
"recipeType": 3
}
],
"stepIndex": 0,
"cookMode": "normal",
"cookStatus": "ready",
"tempUnit": "f",
"preheatSetTime": 0,
"preheatLastTime": 0,
"preheatEndTime": 0,
"preheatTemp": 0,
"startTime": 1698336533,
"totalTimeRemaining": 60,
"currentTemp": 24,
"appointLastTime": 0,
"shakeStatus": 0
}
}
}
This needs to be added to the pyvesync library before it can be integrated with HA. In order to do that, I'll need comprehensive packet captures. Follow the directions https://webdjoe.github.io/pyvesync/latest/development/capturing/ or you can share the device with me temporarily and I will capture the packets.
Would this help?
`POST /cloud/v2/deviceManaged/bypassV2 HTTP/2 host: smartapi.vesync.com user-agent: VeSync/VeSync 5.6.60(CPH2655;Android 16) _signosinfo: Android _packfilesignature: v0001-ae2f6d63d00ae9f5d4b866dd472b54d1c66d98fe159801adb494785d84f3bb2e content-type: application/json; charset=UTF-8 content-length: 1104 accept-encoding: gzip
{"acceptLanguage":"en","accountID":"20xxx776","appVersion":"VeSync 5.6.60","cid":"vssk90b73c6401aba3646b45465cbf","configModule":"WFON_AFR_CAF-P583S-KUS_US","debugMode":false,"method":"bypassV2","phoneBrand":"CPH2655","phoneOS":"Android 16","subDeviceNo":0,"subDeviceType":"","timeZone":"America/Chicago","token":"10010111eyJhbGciOiJIUzI1NiJ9..UNFzFxNFK3fA7fjIq3L7YKGcjYQvXscGxuOwlbVzv_8","traceId":"1765251913564","userCountryCode":"US","deviceId":"vssk90b73c6401aba913646b45465cbf","configModel":"WFON_AFR_CAF-P583S-KUS_US","payload":{"data":{"accountId":"20xxx776","hasLinkage":false,"hasPreheat":0,"hasWarm":false,"mode":"French fries","readyStart":true,"recipeId":6,"recipeName":"French Fries","recipeType":3,"startAct":{"cookSetTime":1200,"cookTemp":385,"preheatTemp":0,"shakeTime":0},"tempUnit":"f"},"method":"startCook","subDeviceType":"","subDeviceNo":0,"source":"APP"}}'
Request 'POST /cloud/v2/deviceManaged/bypassV2 HTTP/2 host: smartapi.vesync.com user-agent: VeSync/VeSync 5.6.60(CPH2655;Android 16) _signosinfo: Android _packfilesignature: v0001-a2af15ba8943e8edf16d1680eed70f57d19ba0026d6a9233ea76e25e3c310c70 content-type: application/json; charset=UTF-8 content-length: 852 accept-encoding: gzip
{"acceptLanguage":"en","accountID":"20xxx776","appVersion":"VeSync 5.6.60","cid":"vssk90b73c6401ab13646b45465cbf","configModule":"WFON_AFR_CAF-P583S-KUS_US","debugMode":false,"method":"bypassV2","phoneBrand":"CPH2655","phoneOS":"Android 16","subDeviceNo":0,"subDeviceType":"","timeZone":"America/Chicago","token":"10010111eyJhbGciOiJIUzI1NiJ9..UNFzFxNFK3fA7fjIq3L7YKGcjYQvXscGxuOwlbVzv_8","traceId":"1765251914109","userCountryCode":"US","deviceId":"vssk90b73c6401aba913646b45465cbf","configModel":"WFON_AFR_CAF-P583S-KUS_US","payload":{"data":{},"method":"getAirfryerStatus","subDeviceType":"","subDeviceNo":0,"source":"APP"}}'
Respond 'HTTP/2 200 date: Tue, 09 Dec 2025 03:45:14 GMT content-type: application/json;charset=UTF-8 content-encoding: gzip
{"traceId":"1765251914109","code":0,"msg":"request success","module":null,"stacktrace":null,"result":{"traceId":"1765251914109","code":0,"result":{"stepArray":[{"cookSetTime":1200,"cookTemp":385,"mode":"French fries","cookLastTime":1200,"shakeTime":0,"cookEndTime":0,"recipeName":"French Fries","recipeId":6,"recipeType":3}],"stepIndex":0,"cookMode":"normal","cookStatus":"ready","tempUnit":"f","preheatSetTime":0,"preheatLastTime":0,"preheatEndTime":0,"preheatTemp":0,"startTime":1765251913,"totalTimeRemaining":1200,"currentTemp":51,"appointLastTime":0,"shakeStatus":0,"linkageStatus":0}}}'
@webdjoe After capturing data I have a working python script to start, status and stop and the 11 presets
#!/usr/bin/env python3
"""
VeSync Air Fryer Control – FINAL VERSION
Works perfectly with Cosori Dual Blaze CAF-P583S-KUS (and similar 2025 models)
"""
import asyncio
import json
import os
import sys
import traceback
from pyvesync import VeSync
# Exact recipe map from official app + your model
RECIPE_MAP = {
"air fry": {"id": 14, "type": 3, "name": "Air Fry", "mode": "AirFry"},
"broil": {"id": 17, "type": 3, "name": "Broil", "mode": "Broil"},
"roast": {"id": 13, "type": 3, "name": "Roast", "mode": "Roast"},
"bake": {"id": 9, "type": 3, "name": "Bake", "mode": "Bake"},
"reheat": {"id": 16, "type": 3, "name": "Reheat", "mode": "Reheat"},
"steak": {"id": 1, "type": 3, "name": "Steak", "mode": "Steak"},
"seafood": {"id": 3, "type": 3, "name": "Seafood", "mode": "Seafood"},
"veggies": {"id": 15, "type": 3, "name": "Veggies", "mode": "Veggies"},
"french fries": {"id": 6, "type": 3, "name": "French Fries", "mode": "FrenchFries"},
"frozen": {"id": 5, "type": 3, "name": "Frozen", "mode": "Frozen"},
"chicken": {"id": 2, "type": 3, "name": "Chicken", "mode": "Chicken"},
}
API_URL = "/cloud/v2/deviceManaged/bypassV2"
async def main() -> None:
action = sys.argv[1].lower() if len(sys.argv) > 1 else "status"
email = os.getenv("VESYNC_EMAIL")
password = os.getenv("VESYNC_PASSWORD")
if not email or not password:
print(json.dumps({"error": "Missing VESYNC_EMAIL/VESYNC_PASSWORD"}))
return
manager = VeSync(email, password, "America/Chicago")
try:
if not await manager.login():
print(json.dumps({"error": "Login failed"}))
return
await manager.get_devices()
device = manager.devices.air_fryers[0] if manager.devices.air_fryers else None
if not device:
print(json.dumps({"error": "Air fryer not found"}))
return
# ──────────────────────────────────────
# START
# ──────────────────────────────────────
if action == "start":
temp = int(sys.argv[2]) if len(sys.argv) > 2 else 375
time_mins = int(sys.argv[3]) if len(sys.argv) > 3 else 10
mode_input = sys.argv[4] if len(sys.argv) > 4 else "Air Fry"
recipe = RECIPE_MAP.get(mode_input.lower())
if not recipe:
print(json.dumps({
"action": "start", "success": False,
"error": f"Unknown mode: {mode_input}",
"available_modes": [k.title() for k in RECIPE_MAP]
}))
return
# 1. startCook – preset configuration
config_body = device._build_request()
config_body.update({
"method": "bypassV2",
"payload": {
"data": {
"accountId": manager.account_id,
"hasLinkage": False,
"hasPreheat": 0,
"hasWarm": False,
"mode": recipe["mode"],
"readyStart": False,
"recipeId": recipe["id"],
"recipeName": recipe["name"],
"recipeType": recipe["type"],
"startAct": {
"cookSetTime": time_mins * 60,
"cookTemp": temp,
"preheatTemp": 0,
"shakeTime": 0
},
"tempUnit": "f"
},
"method": "startCook",
"source": "APP"
}
})
resp, _ = await manager.async_call_api(API_URL, "post", json_object=config_body)
if not (resp and resp.get("code") == 0 and resp.get("result", {}).get("code") == 0):
print(json.dumps({
"action": "start", "success": False,
"error": "Failed to configure device", "api_response": resp
}))
return
await asyncio.sleep(1)
# 2. setSwitch start
start_body = device._build_request()
start_body.update({
"method": "bypassV2",
"payload": {"data": {"startStop": "start"}, "method": "setSwitch", "source": "APP"}
})
start_resp, _ = await manager.async_call_api(API_URL, "post", json_object=start_body)
# 3. readyStart = true
ready_body = device._build_request()
ready_body.update({
"method": "bypassV2",
"payload": {"data": {"readyStart": True}, "method": "updateDeviceStatus", "source": "APP"}
})
await manager.async_call_api(API_URL, "post", json_object=ready_body)
print(json.dumps({
"action": "start", "success": True,
"mode": recipe["name"], "temp": temp, "time": time_mins
}))
# ──────────────────────────────────────
# STOP
# ──────────────────────────────────────
elif action == "stop":
body = device._build_request()
body.update({
"method": "bypassV2",
"payload": {"data": {}, "method": "endCook", "source": "APP"}
})
r, _ = await manager.async_call_api(API_URL, "post", json_object=body)
if not (r and r.get("code") == 0):
body["payload"]["method"] = "setSwitch"
body["payload"]["data"] = {"startStop": "stop"}
r, _ = await manager.async_call_api(API_URL, "post", json_object=body)
print(json.dumps({"action": "stop", "success": bool(r and r.get("code") == 0)}))
# ──────────────────────────────────────
# STATUS – now 100% accurate using the real API call
# ──────────────────────────────────────
elif action == "status":
status_body = device._build_request()
status_body.update({
"method": "bypassV2",
"payload": {
"data": {},
"method": "getAirfryerStatus",
"source": "APP"
}
})
resp, _ = await manager.async_call_api(API_URL, "post", json_object=status_body)
if not resp or resp.get("code") != 0 or resp.get("result", {}).get("code") != 0:
print(json.dumps({"cook_status": "error", "error": "Failed to get status"}))
return
data = resp["result"]["result"]
# Extract only what we care about – exactly like the official app
step = data["stepArray"][0] if data["stepArray"] else {}
status = {
"cook_status": data.get("cookStatus", "unknown"),
"current_temp": data.get("currentTemp"),
"set_temp": step.get("cookTemp"),
"time_remaining_sec": data.get("totalTimeRemaining", 0),
"time_remaining_min": round(data.get("totalTimeRemaining", 0) / 60, 1),
"active_mode": step.get("mode", "N/A"),
"recipe_name": step.get("recipeName", "Manual"),
"device_status": "online",
"connection_status": "connected"
}
print(json.dumps(status))
except Exception as e:
print(json.dumps({"error": str(e), "traceback": traceback.format_exc()}))
finally:
if manager and hasattr(manager, "session"):
try:
await manager.session.close()
except:
pass
if __name__ == "__main__":
asyncio.run(main())