feat: add Svakom Fatima Pro support#908
Conversation
| Endpoint::Rx, | ||
| )) | ||
| .await?; | ||
| // Initialization handshake (consistent across two separate captures). The |
There was a problem hiding this comment.
Interesting. Are all the packets required, or is the 0x55, 0x00 enough?
There was a problem hiding this comment.
Great question — tested it on hardware (raw BLE writes to FFE1, bypassing buttplug's initializer, after full cold boots).
None of the handshake is required to drive the device — not just 0x55,0x00. After a cold power-cycle, with zero init packets (and without even subscribing to the FFE2 notify), a bare connect + write to FFE1 drives all three actuators: vibrate (55 03…) and thrust (55 08…) on one peripheral, suction (55 09…) on the other. Reproduced across several cold boots, each confirmed by physical actuation.
So my PR note ("the device does not respond to control commands without it") is wrong — that came from the capture (the app always sends it on connect), not from testing necessity. Apologies for the bad claim. What the handshake actually is, FWIW: the two 55 04 00 00 … aa packets each elicit a 55 80 00 01 00 46 reply on FFE2 — looks like a status/battery read on connect (0x46≈70, plausibly battery %). 55 00 gets no reply. Neither gates actuation. So I'd suggest dropping the initializer entirely — connect + write is all the device needs. (If battery reporting is ever wanted, that 55 04 → 55 80 exchange is the hook — separate feature.) Happy to push that; let me know if you'd rather keep it for app-parity. Thanks for the nudge to verify!
There was a problem hiding this comment.
Ok, so without that initialisation step, the protocol looks a lot like some of the other Svakom protocols (but with heating). I'd check the other implementations and see if this could just be reduced to adding heating to an existing protocol + config.
There was a problem hiding this comment.
Makes sense — with the init gone it's the standard Svakom 0x55 command family, just with heating. I'll fold it into an existing Svakom protocol + config (plus the temperature actuator) and push a revised version. Any implementation you'd point me at as the closest match? Thanks!
There was a problem hiding this comment.
Not without examining each one. And I'm not in front of the code at the moment to do so quickly.
Following up on #907 — opening the device-support PR you green-lit. Thanks again for the quick reply! 🙏
Summary
Adds protocol support for the Svakom Fatima Pro (advertised BLE name
SL278B).The device is currently unsupported, so Intiface scans past it.
The protocol was derived entirely from a BLE packet capture of the official app (ATT writes on FFE1, notifications on FFE2). It follows the same
0x55-prefixed command family as the existing Svakom Sam / Sam Neo 2 protocols.Four functions are exposed:
Vibrate[0,10]55 03 00 00 01 <0-10>55 03 00 00 00 00Constrict[0,10]55 09 00 00 01 <0-10>55 09 00 00 00 00Oscillate[0,7]55 08 00 00 <pattern 1-7> ff55 08 00 00 00 00Temperature[0,1]55 05 01 37 02 00 0055 05 00 00 02 00 00On connect, an initialization handshake is sent (identical across two separate captures); the device does not respond to control commands without it:
Vibration and suction byte ranges are confirmed against the capture (e.g. vibration max
55 03 00 00 01 0a), and all 7 thrust patterns plus the off bytes match the capture byte-for-byte.Files
crates/buttplug_server/src/device/protocol_impl/svakom/svakom_fatima.rs— protocol handler + initializercrates/buttplug_server_device_config/device-config/protocols/svakom-fatima.yml— device configcrates/buttplug_tests/tests/util/device_test/device_test_case/test_svakom_fatima.yaml— regression fixtureRegistration:
pub mod svakom_fatima;insvakom/mod.rs, theSvakomFatimaIdentifierFactoryentry inprotocol_impl/mod.rs, and thetest_svakom_fatima.yamltest case intest_device_protocols.rs.Design tradeoffs (disclosed)
A few aspects of this device do not map cleanly onto Buttplug's continuous-actuator model. These are deliberate choices, documented here for review:
1. Thrust = pattern-as-intensity (
Oscillate)The thrust function does not expose a continuous speed. The firmware only offers a set of discrete patterns (1–7), selected with
55 08 00 00 <pattern> ff. There is no continuous-speed command in the protocol.Rather than hide the function, the
Oscillatevalue is mapped directly onto the pattern number ([0,7], with 0 = off). So an Oscillate value of, say, 3 selects firmware pattern 3, not "30% of full speed." This is a faithful representation of what the hardware actually does, at the cost ofOscillatenot behaving like a linear intensity for this device.2. Heat = on/off only (
Temperature[0,1])The heat function is modeled as
Temperaturewith a[0,1]range, i.e. on/off. The on/off bytes match the capture and the official app exactly.In practice the hardware heating is imperceptible even on a fully charged device — this matches the behavior of the official app, so the implementation is faithful to both the capture and the app. The capture does contain an additional unmodeled byte position (
55 05 01 37 **01/02** ...) that may correspond to a second heat level; only the02variant is used here, as on/off, which is what the app exercises.Note: the heat actuator is not in the regression fixture, because the device-test Scalar harness (v4) does not accept a
Temperatureactuator. The handler is implemented and the byte sequences are verified faithful to the capture and the official-app behavior, but the heat path is not regression-tested on its own actuator. This is called out plainly so reviewers know its verification status.3. Two independent peripherals — do not hardcode index → function
The Fatima Pro presents as two independent
SL278BBLE peripherals. Each one registers as its own Buttplug device and is driven independently by this protocol — this is the standard Buttplug model for separate BLE peripherals, and they are not forced into a single device.Two things to be aware of:
index -> functionmapping.Both peripherals advertise the same name and speak the same protocol, so the config applies to either; what each physically actuates is determined by the hardware unit that happened to connect, not by index.
Testing
cargo test -p buttplug_tests fatima— protocol regression (vibration / suction / thrust + off paths; heat excluded per the note above).Provenance / licensing
All bytes here come from observing the device's own BLE traffic (a packet capture of the official app). No vendor proprietary code was reverse-engineered or decompiled — this is a clean BLE command encoding, in the same style as the existing Svakom protocols in this repo.
Closes #907