Repository navigation
Conversation
Parameters only recognised a 00 prefix, so a reply such as 4827 09 a1021e00 parsed to an empty dict. Any first byte below the first TLV tag (0xa1) is now the reply's status, exposed as ParameterDict.status; pushes have none. Building also wrote no prefix: the If condition combined two construct expressions with Python's `or` at import time, which left only `this._parsing`. A parsed reply now builds back to the same bytes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Frames are routed by the decoded pattern (composer, channel) and command (0x80 fragmented, 0x40 encrypted, 12-bit message type) instead of whole patterns: - channel 0x01 reaches the negotiation on either composer, so the 030101 grant does too - session frames on either composer reach futures and telemetry; 03000f replies are no longer dropped - other channels are logged and dropped - fragments are reassembled on the 0x80 flag instead of a 253-byte length - a session frame is decrypted once, on the 0x40 flag; a second future no longer gets a double decrypt - _process_session is the one dispatch point for session frames SolixBLE.transport adds NegotiatingTransport (ff09) and LegacyTransport (1780, the flip-dots#64 transport); _TRANSPORT selects the GATT characteristics, a legacy-transport device skips negotiation, and discover_devices matches either service. SolixBLE.advertisement decodes the 0xffff record with a construct, and SolixBLE.factory picks the model class from it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
I was checking some of the older tickets, and #1 included some plaintext 8405, which further confirms the |
From the advertisements in SolixBLE and HaSolixBLE issues: product types b112 (F3800 Plus, the A1790's command and telemetry map), b006 (Solarbank 2 E1600 Pro), b103 (C800; A1753/4/5 share one map) and b119 (C1000 Plus / X Gen 2, the A1763's display-board build), and the advertised model names. The A1763 part-number entry goes: that model advertises "SOLIX C1000 Gen 2". The A1340 Prime power bank advertises service 2215 and uses 22150002/22150003 characteristics with the ff09 framing and negotiation; it gets Transport2215, and the factory returns None for it as for the legacy transport until a class exists. discover_devices also matches the 0xffff record, which a passive scan carries without the services, and uses the scanner it is given. Telemetry is matched on the 12-bit message type, so a clear fragmented 8405 (SolixBLE #1) is read as the c405 a model lists; the always-telemetry type stays 0x300. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Those same #1 frames also showed the telemetry match was on the whole wire command, so a clear
The F3800 / F3800 Plus and C800 mappings are untested on BLE; if an owner can try |
|
Since #77 is just a +14/-4 in the code and a new file in tests, I'm submitting this for review as well @flip-dots |
Second of four stacked PRs replacing #70; based on the status-byte PR. The existing negotiation code is unchanged and runs on the new routing.
Frames are now routed by their decoded header instead of whole patterns. Pattern
03 <composer> <channel>: the channel is the device's dispatch key (0x01negotiation,0x0f/0x11session) and the composer says whether the device sent the frame on its own (01) or echoed the request (00). The command's high nibble carries the frame's own state:0x80fragmented,0x40encrypted.SolixBLE/constructs.py:PacketPattern,PacketCommand(flags + 12-bit message type; also builds), and the channel / composer constants.SolixBLE/device.py:0x01goes to negotiation whatever the composer, so the grant a device pushes on030101after its button is pressed reaches the negotiation;03000freplies are no longer dropped;0x80is set, not when a frame happens to be 253 bytes long;0x40is set; with two futures on one frame the second no longer gets a double decrypt;_process_session(cmd, payload, encrypted=...)is the one place a session frame is dispatched, for later device-specific frame types to extend.legacy_f2000branch, so Add support for older F2000/767 firmware version #64 can sit on top):SolixBLE/transport.pyNegotiatingTransport(ff09,8c850002/8c850003) andLegacyTransport(1780,7777/8888).SolixBLEDevice._TRANSPORTselects one;UUID_TELEMETRY/UUID_COMMANDfollow it, and on the legacy transportconnect()sends no negotiation andnegotiatedis True once connected.const.UUID_IDENTIFIERSlists both services anddiscover_devices()matches either. (Onlegacy_f2000,discover_devicescalls.intersectionon that tuple, which raisesAttributeError; this PR uses a membership test.)SolixBLE/advertisement.py: the0xffffmanufacturer record as a construct;capability_from_advertisement().SolixBLE/factory.py:device_class_from_advertisement()picks the model class from the advertised product type, else a Prime-style name<part number>_<MAC4>, elseGeneric; None on the legacy transport.docs/source/protocols.rst"Transports" (packet layout, channels, transports, advertisement); the two helpers inhelpers.rst.test_constructs.py,test_routing.py(grant routing, unhandled channel, flag-driven decryption and reassembly, two futures),test_transport.py,test_advertisement.py,test_factory.py.MockDevicerecords the GATT UUIDs it is subscribed to and written to.Suite: 400 passed (base: 353). mypy
--strict: 207 errors, one fewer than the base; ruff adds nothing to the existing files.