Skip to content

feat: add Cosori Pro II 5.8 Qt (CS358-AF) air fryer - #557

Open
ScribleSean wants to merge 3 commits into
webdjoe:air-fryer-refactorfrom
ScribleSean:feat/cs358-af
Open

ScribleSean wants to merge 3 commits into
webdjoe:air-fryer-refactorfrom
ScribleSean:feat/cs358-af

Conversation

@ScribleSean

@ScribleSean ScribleSean commented Oct 11, 2026 •

Copy link
Copy Markdown

Adds the Cosori Pro II 5.8 Qt (CS358-AF, US) on top of the air fryer refactor. Refs #526.

The model speaks the same bypassV2 protocol as the TurboBlaze, so VeSyncTurboBlazeFryer drives it. The change is one AirFryerMap entry, a cook_from_preheat implementation for that class, and tests.

What changed

  • device_map.py: new AirFryerMap entry for CS358-AF.
  • const.py: BACON, SHRIMP and TOAST added to AirFryerCookModes.
  • vesynckitchen.py: VeSyncTurboBlazeFryer.cook_from_preheat(), which sends the preheatCook payload method with empty data. It requires the PREHEAT feature and a preheatEnd status.
  • Tests: CS358-AF added to the fryer details tests, plus test_fryer_preheat.py for the new method and the temperature range.

Tested on a physical unit

I have a CS358-AF (configModule WiFiBTOnboarding_AirFryer_CS358-AF_US, Fahrenheit) and ran this branch against it. Every row below was observed on the device through this code.

Action Result
update() getAirfryerStatus returns the same fields as FryerTurboBlazeDetailResult, plus appointLastTime
set_mode(cook_time, cook_temp) startCook starts cooking from the cloud, no button press on the unit
set_mode(..., preheat_time=...) starts preheating; the cook step stays in stepArray for the whole preheat
cook_from_preheat() at preheatEnd preheatCook starts the queued cook step at the same temperature and time
cook_from_preheat() after pulling and returning the basket at preheatEnd same result; status goes preheatEnd, pullOut, preheatEnd, then cooking
end() while preheating and while cooking endCook returns the unit to standby

Statuses observed, all included in the status_map:

Status When
standby idle
ready program staged, waiting for the Start button
heating preheating
preheatStop Start/Pause pressed on the unit during preheat
preheatEnd preheat finished, waiting
cooking cooking
cookStop Start/Pause pressed on the unit during a cook
cookEnd cook finished on its own
pullOut basket removed, in any phase
keeping Keep Warm running

Other things read off the unit:

  • The unit picks its own preheat duration from the target temperature: 3 minutes at 170 F and 4 minutes at 350 F. The value passed as preheat_time only switches preheat on.

  • A manual program reports mode Custom, which matches AirFryerPresets.custom.

  • The unit reports nothing for a preset until it is started. Starting each preset button on the unit gave these mode strings, each with its own recipe ID:

    Button API mode recipeId
    Steak Steak 1
    Chicken Chicken 2
    Seafood Seafood 3
    Bacon Bacon 4
    Frozen Foods Frozen 5
    French Fries French fries 6
    Vegetables Vegetables 7
    Shrimp Shrimp 8
    Bake Bake 9
    Toast Toast 10
    Keep Warm empty string 0

    Only the mode strings are in the map. The Shake button on the unit sets shakeTime in the step to about half the cook time, and back to 0 when turned off.

  • Temperature moves in 10 F steps on the unit. The manual gives 170 to 400 F and 77 to 204 C. I set the Celsius range to 80 to 200 so both ends sit on the 5 C step and can be set. I only ran the unit in Fahrenheit.

Things to flag

  • cook_from_preheat only reaches the API on the CS358-AF. The CAF-DC601S declares PREHEAT but has no status that maps to PREHEAT_END, and the Dual Blaze entries do not declare PREHEAT, so both return False before any request.
  • currentTemp was 0 in every state on my unit, including while cooking. With the Celsius conversion in get_details() that surfaces as 32 F. I left the shared code alone, but the reading is not useful on this model.
  • The fries preset reports French fries, with a space and a lower-case f, where the Dual Blaze uses FrenchFries.
  • Presets were only read, not started from the cloud. I started them from the unit's buttons. Cloud starts were tested with Custom only.
  • No cloud pause or resume. The unit can be paused from its own button or by pulling the basket, but I do not know a payload method for it, so RESUMABLE is not claimed.

Existing behaviour in the shared class that this model runs into

I did not change any of these here, to keep the diff to the new model. The first two are fixed separately in #560.

  • set_mode_from_recipe sets the local status to COOKING even when preheat was requested, so cook_from_preheat() needs an update() first to see preheatEnd.
  • get_details passes preheatSetTime through as 0, not None. FryerState.is_in_preheat_mode tests for None, so a pullOut during a cook reads as preheat.
  • set_state returns early for COOK_STOP and PREHEAT_STOP, so a first poll in ready, cookStop or preheatStop has no cook temperature, mode or remaining time.

Checks

  • pytest src/tests: 407 passed, 3 skipped. The test workflow does not run for pull requests against this branch, so that is a local result.
  • ruff check and ruff format --check are clean. mypy reports two errors in vesyncthermostat.py, the same as on the base branch.

I can run further captures or test changes against the unit if that helps.

Map the CS358-AF to VeSyncTurboBlazeFryer and add cook_from_preheat to
that class using the preheatCook payload method.

All values in the map entry and the new method were confirmed on a
physical US unit. Refs webdjoe#526.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant