Skip to content

Device support: Yiciyuan stroker family (FJB-03, YS-TD-01/02/03) #888

Description

@SanJerry007

Stacked follow-up to #887. Adds the rest of the Yiciyuan stroker family by translating the official app's mixins/os_base.js model-dispatch logic, with each model marked as source-extracted but not hardware-verified.

This is filed as a separate issue (and will land as a separate PR) so #887 can ship on its own merit if FJB-03's distinct wire format raises concerns.

What's added

Model Wire frame Stroke range Vibe / axis_c range
YCY-FJB-03 6-byte: [0x35, 0x12, stroke, vibe, axis_c, sum&0xFF] 0..=40 (doubled) 0..=20
YS-TD-01 / -02 / -03 16-byte: [0x35, 0x12, stroke, vibe, axis_c, 0×11] 0..=20 0..=20

Same BLE service / characteristic UUIDs (ff40 / ff41 / ff42) across the whole family — the app uses a unified _ble_write_os writer that varies the payload encoding per model, not the BLE topology.

App-source evidence

FJB-03 wire-format branch (mixins/os_base.js ~ line 257 in the decoded bundle):

"YCY-FJB-03"==a ? (i = (i+Array(10).join(\"0\")).slice(0,10), i += this._checksum(i))
                : (i+Array(32).join(\"0\")).slice(0,32)

Translation: when the connected device's localName is YCY-FJB-03, pad the hex payload to 10 chars (5 bytes), then append the modular checksum (2 hex chars). Otherwise pad to 32 chars (16 bytes), no checksum.

_checksum from mixins/pump.js (also inherited by os_base.js):

_checksum: function(n) {
  n = n.replace(/\s+/g, \"\");
  var i = 0;
  for (var e = 0; e < n.length; e += 2) i += parseInt(n.substr(e, 2), 16);
  return (255 & i).toString(16).padStart(2, \"0\").toUpperCase();
}

i.e. sum every 2-hex-char byte, mod 256, hex-encode. Matched in yiciyuan.rs as body.iter().fold(0u16, |acc, b| acc + *b as u16) as u8.

Stroke range branch (same file, near the level-clamping block):

var A = \"YCY-FJB-03\" == info?.localName;
t < 0 && (t = A ? 20+Math.abs(t) : Math.abs(t)),
A ? t > 40 && (t = 40) : t > 20 && (t = 20)

i.e. FJB-03 clamps to 0..=40, other models clamp to 0..=20. (The negative-stroke handling for reverse direction is omitted from this PR — Buttplug's Oscillate is unsigned. Forward-only stroke is what the YAML exposes.)

YS-TD family routing:

{ suffix: \"_td\", vuexPrefix: \"bt_os_td\",
  deviceNames: [\"YS-TD-01\", \"YS-TD-02\", \"YS-TD-03\"], sbType: \"td\" }

_ble_write_os_td is dynamically generated by mixins/os_base.js from the same _ble_write_os${suffix} factory; the suffix only changes vuex namespacing, not the wire path. YS-TD models fall through to the "else" branch above → 16-byte frame.

Implementation

Branch SanJerry007:add-yiciyuan-extended (depends on #887's branch landing first):

  • protocol_impl/yiciyuan.rs gains a is_fjb03 flag stamped by YiciyuanInitializer::initialize from hardware.name(). Per-axis clamp and build_packet() branch on it; no allocation overhead for the common case.
  • device-config-v4/protocols/yiciyuan.yml adds 4 new configurations entries (FJB-03, YS-TD-01/02/03) under the existing defaults block and 4 names into the btle names list.
  • Two new test fixtures register against test_device_protocols (FJB-03 with checksum verification + max-range demo; one YS-TD-01 to verify the new identifier route).

848 / 848 device protocol tests pass (cargo test -p buttplug_tests --test test_device_protocols).

Explicit caveats

  • I own and tested FJB-01. FJB-03 and YS-TD-0x have NOT been physically verified. The protocol module is correct against the app's reverse-engineered source code; that does not guarantee against firmware quirks the app source happens to not exercise.
  • If reviewer prefers untested device identifiers to live as commented-out YAML stubs or as a # UNVERIFIED: section, I can restructure.
  • Happy to drop FJB-03 (the model with the most unique code path) and ship only the YS-TD trio if there's a preference for the safer subset.

Open questions

  1. OK to land as-is, with the model-detection-at-connect pattern + is_fjb03 flag? Alternative is splitting into yiciyuan.rs and yiciyuan_fjb03.rs (analogous to galaku.rs vs galaku_pump.rs) — happy to do that if you prefer.
  2. Any blockers on landing source-extracted-but-unverified configurations in principle?
  3. Pump variants (YISK-003V3 plaintext + YISKJ-003 / -003V2 AES with hard-coded key) are also source-extracted but need an HCI capture to nail down the BLE service UUID — I'd rather not guess there. Would a third follow-up PR be welcome once that's pinned down?

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions