Repository navigation
Conversation
The CAF-LI401S uses the same bypassV2 protocol as the TurboBlaze (getAirfryerStatus / startCook / endCook, with cook parameters carried in stepArray[stepIndex]), so VeSyncTurboBlazeFryer drives it without a new device class. Adds a single AirFryerMap entry. Verified against physical hardware: discovery, live cook state parsing (status, mode, set temperature, countdown, chamber temperature) and end(). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds standby and cooking fixtures, recorded bypassV2 API request YAML and parametrised detail tests for the CAF-LI401S, per the contributing guide's requirement that each new device include tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The initial entry copied its cook modes and default preset from the Dual Blaze, which does not match this model. Read the real values off a physical unit by staging each preset button and reading stepArray: Custom 175 C / 15 min Chicken 210 C / 13 min Fries 200 C / 17 min Bacon 160 C / 12 min Steak 160 C / 15 min Veggies 200 C / 8 min Warm 75 C / 5 min Corrections: - the fries preset reports 'Fries', not 'FrenchFries' - adds Bacon, which had no AirFryerCookModes member - adds Warm; drops AirFry, Bake, Roast, Seafood, Frozen and Reheat, none of which the unit exposes - default_preset was AirFryerPresets.air_fry (400 F AirFry), which this Celsius-only model cannot accept; adds a Celsius manual preset instead - records that 'standby', 'cooking' and 'heating' are observed, and that preheat reports an empty stepArray Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Celsius and Fahrenheit ranges are sensible rounded bounds rather than exact conversions of each other, matching the values used in webdjoe#537. The temp_unit in the map is only the pre-poll default; the unit the device reports appears to follow account region rather than a user setting, so a US unit of this model may report 'f'. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Drops the device-specific manual preset. AirFryerPresets.custom already carries the mode, recipe ID and recipe type this unit reports, and its target_temp/cook_time/temp_unit are never sent: every caller overwrites them via prepare_temperature() and convert_time_for_api(), and the request takes tempUnit from the device's live unit. const.py is now a single added enum member for the Bacon cook mode. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The EU unit read here reports 'Fries' where the Dual Blaze entries use 'FrenchFries'. The EU Dual Blaze entry appears derived from the US one rather than confirmed against hardware, so it is not independent evidence, and per-model versus regional cannot be distinguished from one device. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The per-preset temperature, time and shake time read from the test device are user-editable in the VeSync app, so they describe that owner's configuration rather than the model. Only the mode strings, which are fixed API identifiers, are recorded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Owner
|
@glombek Thank you for the hard work on this PR. Can you send the full packet captures? I made some updates to the refactor branch that should support most of this API structure. Please reach out to me on discord with the packet captures: homefire1608 |
6 of 13 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the Cosori Lite 3.8L (
CAF-LI401S, EU) to the air fryer refactor.No new device class — the model speaks the same bypassV2 protocol as the
TurboBlaze (
getAirfryerStatus/startCook/endCook, cook parameterscarried in
stepArray[stepIndex]), soVeSyncTurboBlazeFryerdrives it as-is.The change is one
AirFryerMapentry, one new cook mode member and tests.I based this on #503 rather than #537, since the refactor here already
generalises everything that PR implements by hand.
Everything below was read off a physical unit
I own a CAF-LI401S and took every value in this entry from it rather than
inferring from the other fryers. That mattered — my first draft copied the Dual
Blaze modes and was wrong in several ways.
Cook modes. Staging each preset button on the unit and reading back
stepArraygives these mode strings:modeCustomChickenFriesBaconSteakVeggiesWarmOnly the mode strings are recorded. The temperature, time and shake time behind
each preset are user-editable in the VeSync app, so what a given device reports
is that owner's configuration rather than a device default — not something worth
baking into the map.
Points worth flagging:
Fries, not theFrenchFriesstring the DualBlaze uses
Baconhad noAirFryerCookModesmember, so this adds onerecipeId1, preset or manual — the unit does notvary the recipe ID the way the Dual Blaze does
Default preset.
AirFryerPresets.air_fry(modeAirFry) is not usablehere, as that mode does not exist on the unit.
AirFryerPresets.customis usedinstead: its mode, recipe ID and recipe type match what the device reports, and
its
target_temp/cook_time/temp_unitnever reach the wire — callersoverwrite them via
prepare_temperature()andconvert_time_for_api(), and therequest takes
tempUnitfrom the device's live unit.FriesvsFrenchFries. Handled through the per-devicecook_modesmaprather than a new enum member:
AirFryerCookModes.FRENCH_FRIESpoints at'Fries'here and stays'FrenchFries'for the Dual Blaze. OnlyBaconneededa new member, having none at all.
Worth flagging that I cannot tell whether that split is per model or
regional. The unit I read is an EU CAF-LI401S. The Dual Blaze entries both use
'FrenchFries', but theCAF-P583S-KEUentry looks derived from the-KUSonerather than independently confirmed — it switches
temp_unitto Celsius whilekeeping
temperature_step_f=5— so it isn't independent evidence for the EUside. If the alias turns out to follow region rather than model, the other
entries' mode strings would need the same scrutiny. Anyone with a US
CAF-LI401S, or a hardware-confirmed EU Dual Blaze, could settle it quickly; I
have only the one device and did not want to generalise from it.
Temperature range. Both
temperature_range_candtemperature_range_farepopulated as the other entries do, using the same rounded bounds as #537 rather
than exact conversions of one another.
min_temp/max_tempresolve from thepair against the live
temp_unit, so this stays correct whichever unit thedevice reports. My unit reports
"c"with no user-facing setting to change it,so the unit looks to follow account region;
temp_unitin the map is only thepre-poll default.
Cook status.
standby,cookingandheatingare confirmed from thedevice. The rest of
status_mapis carried over from the other bypassV2 fryersand marked in a comment as unobserved, rather than presented as verified.
Live cook
Parsed state during a cook I started at the appliance:
current_temptracked the chamber through the heat-up (30 → 125 → 150 °C) andthe countdown decremented correctly.
end()also works:The fryer physically stopped, which I confirmed at the unit.
One thing for you to weigh in on
1. Preheat reports an empty
stepArray, so it reads as standby.While preheating, the unit returns no cook step at all:
VeSyncTurboBlazeFryer.get_details()returns early onnot resp_model.stepArrayand calls
set_standby(), so an actively heating fryer is reported as standbyand
preheatSetTime/preheatTempare discarded.I have deliberately not claimed
AirFryerFeatures.PREHEATin this entry,since the class cannot currently surface it, and I have not changed
get_details()because that is shared behaviour and I can only test one of thethree devices it serves. Happy to send a fix if you tell me the shape you want —
the obvious one is to fall through to a preheat branch when
stepArrayis emptybut
preheatSetTimeis set.Tests
Added per the contributing guide, so the new device is covered rather than just
registered:
call_json_fryers.py, shaped from my device'sreal
getAirfryerStatuspayload (including thehasPro/linkageStatuskeys it returns)
CAF-LI401S.update.standbyand.cookingcases intest_fryers.pysrc/tests/api/vesynckitchen/CAF-LI401S.yaml, writtenwith
pytest --write_apiChecks
Run in a venv created with
pip install -e .[dev]:pytestpylint src/pyvesyncruff check src/pyvesyncmypy src/pyvesyncvesyncthermostat.py)flake8 src/pyvesyncfryer_models.py:42E501)The mypy and flake8 findings are identical with and without my change; I left
them alone as they're out of scope here.