Fix Prime 250w charger. - #57
Conversation
|
Hi @flip-dots With MQTT, the A2345 has not only the realtime trigger that runs only about 8-10 seconds with a subset of telemetry, there is also a status request command that triggers only a single but full telemetry message, containing also the live power values (more than the realtime trigger message). If you want to automate telemetry for A2345, I suggest to investigate and use the status request rather than the realtime data, since it gives you control about the message frequency and you receive also the device settings, which are not included in the real time messages. Example of status request 0200: ff:09:14:00:03:00:0f:02:00:a1:01:22:fe:05:03:64:67:4e:6a:b1
15:06:12 INFO: ----------------------- Charger / A2345 / 0200 / Header ------------------------
ff 09 : 2 Byte Anker Solix message marker (supposed 'ff 09')
14 00 : 2 Byte total message length (20) in Bytes (Little Endian format)
03 00 0f: 3 Byte fixed message pattern (supposed `03 00/01 0f` for send/receive)
02 00 : 2 Byte message type pattern (varies per device model and message type)
: 1 Byte optional message increment ( 0)
-> b1 <-: XOR Checksum Byte (last message byte), Crosscheck: 00 => OK
-- Fields --|- Value (Hex/Decode Options)---------------------------------------
Fld Len Type uIntLe/var sIntLe floatLe dblLe/4int
a1 01 -- 22
└-> 1 unk b'"' --> pattern_22
fe 05 03 64:67:4e:6a
└-> 5 var 26468;27214 1783523172 62381741815689138600411136.000 100;103;78;106 --> msg_timestamp (2026-07-08 15:06:12)
--------------------------------------------------------------------------------The A2345 will respond with message 0a00, example including the known decoding of the fields ----------------------- Charger / A2345 / 0a00 / Header ------------------------
ff 09 : 2 Byte Anker Solix message marker (supposed 'ff 09')
2f 01 : 2 Byte total message length (303) in Bytes (Little Endian format)
03 01 0f: 3 Byte fixed message pattern (supposed `03 00/01 0f` for send/receive)
0a 00 : 2 Byte message type pattern (varies per device model and message type)
00 : 1 Byte optional message increment ( 0)
-> 96 <-: XOR Checksum Byte (last message byte), Crosscheck: 00 => OK
-- Fields --|- Value (Hex/Decode Options)---------------------------------------
Fld Len Type uIntLe/var sIntLe floatLe dblLe/4int
a1 01 -- 64
└-> 1 unk b'd'
a2 03 02 7a:07
└-> 3 sile 1914 1914 122;7 --> sw_version
a3 03 02 00:00
└-> 3 sile 0 0 0;0
a4 08 04 01:20:4d:64:00:c5:00
└-> 8 bin b'\x01 Md\x00\xc5\x00' --> {'00': {'name': 'usbc_1_status', 'type': b'\x01'}, '01': {'name': 'usbc_1_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_1_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_1_power', 'type': b'\x02', 'factor': 0.01}}
a5 08 04 01:40:3a:00:00:00:00
└-> 8 bin b'\x01@:\x00\x00\x00\x00' --> {'00': {'name': 'usbc_2_status', 'type': b'\x01'}, '01': {'name': 'usbc_2_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_2_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_2_power', 'type': b'\x02', 'factor': 0.01}}
a6 08 04 01:90:13:9c:00:32:00
└-> 8 bin b'\x01\x90\x13\x9c\x002\x00' --> {'00': {'name': 'usbc_3_status', 'type': b'\x01'}, '01': {'name': 'usbc_3_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_3_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_3_power', 'type': b'\x02', 'factor': 0.01}}
a7 08 04 01:70:13:64:00:31:00
└-> 8 bin b'\x01p\x13d\x001\x00' --> {'00': {'name': 'usbc_4_status', 'type': b'\x01'}, '01': {'name': 'usbc_4_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_4_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_4_power', 'type': b'\x02', 'factor': 0.01}}
a8 08 04 00:00:00:00:00:00:00
└-> 8 bin b'\x00\x00\x00\x00\x00\x00\x00' --> {'00': {'name': 'usba_1_status', 'type': b'\x01'}, '01': {'name': 'usba_1_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usba_1_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usba_1_power', 'type': b'\x02', 'factor': 0.01}}
a9 08 04 01:70:13:00:00:00:00
└-> 8 bin b'\x01p\x13\x00\x00\x00\x00' --> {'00': {'name': 'usba_2_status', 'type': b'\x01'}, '01': {'name': 'usba_2_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usba_2_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usba_2_power', 'type': b'\x02', 'factor': 0.01}}
aa 14 04 01:00:0c:00:00:00:0c:00:00:00:58:02:00:00:00:00:00:00:02
└-> 20 bin b'\x01\x00\x0c\x00\x00\x00\x0c\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00\x02' --> {'00': {'name': 'usbc_1_switch', 'type': b'\x01'}, '01': {'name': 'usbc_1_start_switch', 'type': b'\x01'}, '02': {'name': 'usbc_1_start_hour', 'type': b'\x01'}, '03': {'name': 'usbc_1_start_minute', 'type': b'\x01'}, '04': {'name': 'usbc_1_start_weekdays', 'type': b'\x01'}, '05': {'name': 'usbc_1_end_switch', 'type': b'\x01'}, '06': {'name': 'usbc_1_end_hour', 'type': b'\x01'}, '07': {'name': 'usbc_1_end_minute', 'type': b'\x01'}, '08': {'name': 'usbc_1_end_weekdays', 'type': b'\x01'}, '09': {'name': 'usbc_1_timer_switch', 'type': b'\x01'}, '10': {'name': 'usbc_1_timer_seconds', 'type': b'\x03'}, '14': {'name': 'usbc_1_timer_remaining_seconds', 'type': b'\x03'}, '18': {'name': 'usbc_1_priority', 'type': b'\x01'}}
ab 14 04 01:ff:0c:00:00:ff:0c:00:00:00:58:02:00:00:00:00:00:00:02
└-> 20 bin b'\x01\xff\x0c\x00\x00\xff\x0c\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00\x02' --> {'00': {'name': 'usbc_2_switch', 'type': b'\x01'}, '01': {'name': 'usbc_2_start_switch', 'type': b'\x01'}, '02': {'name': 'usbc_2_start_hour', 'type': b'\x01'}, '03': {'name': 'usbc_2_start_minute', 'type': b'\x01'}, '04': {'name': 'usbc_2_start_weekdays', 'type': b'\x01'}, '05': {'name': 'usbc_2_end_switch', 'type': b'\x01'}, '06': {'name': 'usbc_2_end_hour', 'type': b'\x01'}, '07': {'name': 'usbc_2_end_minute', 'type': b'\x01'}, '08': {'name': 'usbc_2_end_weekdays', 'type': b'\x01'}, '09': {'name': 'usbc_2_timer_switch', 'type': b'\x01'}, '10': {'name': 'usbc_2_timer_seconds', 'type': b'\x03'}, '14': {'name': 'usbc_2_timer_remaining_seconds', 'type': b'\x03'}, '18': {'name': 'usbc_2_priority', 'type': b'\x01'}}
ac 14 04 01:ff:0c:00:00:ff:0c:00:00:00:58:02:00:00:00:00:00:00:01
└-> 20 bin b'\x01\xff\x0c\x00\x00\xff\x0c\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00\x01' --> {'00': {'name': 'usbc_3_switch', 'type': b'\x01'}, '01': {'name': 'usbc_3_start_switch', 'type': b'\x01'}, '02': {'name': 'usbc_3_start_hour', 'type': b'\x01'}, '03': {'name': 'usbc_3_start_minute', 'type': b'\x01'}, '04': {'name': 'usbc_3_start_weekdays', 'type': b'\x01'}, '05': {'name': 'usbc_3_end_switch', 'type': b'\x01'}, '06': {'name': 'usbc_3_end_hour', 'type': b'\x01'}, '07': {'name': 'usbc_3_end_minute', 'type': b'\x01'}, '08': {'name': 'usbc_3_end_weekdays', 'type': b'\x01'}, '09': {'name': 'usbc_3_timer_switch', 'type': b'\x01'}, '10': {'name': 'usbc_3_timer_seconds', 'type': b'\x03'}, '14': {'name': 'usbc_3_timer_remaining_seconds', 'type': b'\x03'}, '18': {'name': 'usbc_3_priority', 'type': b'\x01'}}
ad 14 04 01:ff:0c:00:00:ff:0c:00:00:00:58:02:00:00:00:00:00:00:01
└-> 20 bin b'\x01\xff\x0c\x00\x00\xff\x0c\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00\x01' --> {'00': {'name': 'usbc_4_switch', 'type': b'\x01'}, '01': {'name': 'usbc_4_start_switch', 'type': b'\x01'}, '02': {'name': 'usbc_4_start_hour', 'type': b'\x01'}, '03': {'name': 'usbc_4_start_minute', 'type': b'\x01'}, '04': {'name': 'usbc_4_start_weekdays', 'type': b'\x01'}, '05': {'name': 'usbc_4_end_switch', 'type': b'\x01'}, '06': {'name': 'usbc_4_end_hour', 'type': b'\x01'}, '07': {'name': 'usbc_4_end_minute', 'type': b'\x01'}, '08': {'name': 'usbc_4_end_weekdays', 'type': b'\x01'}, '09': {'name': 'usbc_4_timer_switch', 'type': b'\x01'}, '10': {'name': 'usbc_4_timer_seconds', 'type': b'\x03'}, '14': {'name': 'usbc_4_timer_remaining_seconds', 'type': b'\x03'}, '18': {'name': 'usbc_4_priority', 'type': b'\x01'}}
ae 14 04 01:ff:0c:00:00:ff:0c:00:00:00:58:02:00:00:00:00:00:00:00
└-> 20 bin b'\x01\xff\x0c\x00\x00\xff\x0c\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00\x00' --> {'00': {'name': 'usba_switch', 'type': b'\x01'}, '01': {'name': 'usba_start_switch', 'type': b'\x01'}, '02': {'name': 'usba_start_hour', 'type': b'\x01'}, '03': {'name': 'usba_start_minute', 'type': b'\x01'}, '04': {'name': 'usba_start_weekdays', 'type': b'\x01'}, '05': {'name': 'usba_end_switch', 'type': b'\x01'}, '06': {'name': 'usba_end_hour', 'type': b'\x01'}, '07': {'name': 'usba_end_minute', 'type': b'\x01'}, '08': {'name': 'usba_end_weekdays', 'type': b'\x01'}, '09': {'name': 'usba_timer_switch', 'type': b'\x01'}, '10': {'name': 'usba_timer_seconds', 'type': b'\x03'}, '14': {'name': 'usba_timer_remaining_seconds', 'type': b'\x03'}, '18': {'name': 'usba_priority', 'type': b'\x01'}}
af 06 04 03:7c:14:ae:05
└-> 6 bin b'\x03|\x14\xae\x05' --> {'00': [{'name': 'clock_settings', 'mask': 255}, {'name': 'clock_switch', 'mask': 128}, {'name': 'holiday_switch', 'mask': 64}], '01': {'name': 'theme_id', 'type': b'\x03', 'signed': False}}
b0 02 01 01
└-> 2 ui 1 1 --> display_timeout_mode
b1 02 01 02
└-> 2 ui 2 2 --> usage_mode
b2 03 04 00:00
└-> 3 bin b'\x00\x00'
b3 02 01 55
└-> 2 ui 85 85 --> display_brightness
b4 02 01 00
└-> 2 ui 0 0 --> knob_mode
b5 02 01 01
└-> 2 ui 1 1 --> clock_mode
b6 02 01 01
└-> 2 ui 1 1 --> unknown_b6
b7 03 02 ff:02
└-> 3 sile 767 767 255;2
b8 08 04 00:00:00:00:00:00:ff
└-> 8 bin b'\x00\x00\x00\x00\x00\x00\xff' --> {'00': {'name': 'custom_profile_number', 'type': b'\x01'}, '01': {'name': 'auto_exit_switch', 'type': b'\x01'}, '02': {'name': 'custom_usb_c1_power_limit', 'type': b'\x01'}, '03': {'name': 'custom_usb_c2_power_limit', 'type': b'\x01'}, '04': {'name': 'custom_usb_c3_power_limit', 'type': b'\x01'}, '05': {'name': 'custom_usb_c4_power_limit', 'type': b'\x01'}, '06': {'name': 'custom_usb_a_power_limit', 'type': b'\x01'}}
b9 06 04 08:00:17:01:7f
└-> 6 bin b'\x08\x00\x17\x01\x7f' --> {'00': {'name': 'clock_display_start_hour', 'type': b'\x01'}, '01': {'name': 'clock_display_start_minute', 'type': b'\x01'}, '02': {'name': 'clock_display_end_hour', 'type': b'\x01'}, '03': {'name': 'clock_display_end_minute', 'type': b'\x01'}, '04': {'name': 'clock_display_weekdays', 'type': b'\x01'}}
ba 0d 04 3b:00:00:3b:00:00:3b:00:00:3b:00:00
└-> 13 bin b';\x00\x00;\x00\x00;\x00\x00;\x00\x00' --> {'00': {'name': 'custom_usb_c1_protocols', 'type': b'\x04', 'length': 1, 'state_converter': <function <lambda> at 0x7b88bf2b1fe0>}, '03': {'name': 'custom_usb_c2_protocols', 'type': b'\x04', 'length': 1, 'state_converter': <function <lambda> at 0x7b88bf2b2090>}, '06': {'name': 'custom_usb_c3_protocols', 'type': b'\x04', 'length': 1, 'state_converter': <function <lambda> at 0x7b88bf2b2140>}, '09': {'name': 'custom_usb_c4_protocols', 'type': b'\x04', 'length': 1, 'state_converter': <function <lambda> at 0x7b88bf2b21f0>}}
bb 02 01 01
└-> 2 ui 1 1
bc 02 01 01
└-> 2 ui 1 1
bd 11 04 00:00:00:00:fa:ff:fb:ff:00:00:00:00:00:00:00:00
└-> 17 bin b'\x00\x00\x00\x00\xfa\xff\xfb\xff\x00\x00\x00\x00\x00\x00\x00\x00'
fe 05 03 0a:5b:56:69
└-> 5 var 23306;26966 1767267082 16196252825932032387317760.000 10;91;86;105 --> msg_timestamp (2026-01-01 11:31:22)
--------------------------------------------------------------------------------Example of 0303 real time message, only showing a subset of fields (including decoding): ----------------------- Charger / A2345 / 0303 / Header ------------------------
ff 09 : 2 Byte Anker Solix message marker (supposed 'ff 09')
63 00 : 2 Byte total message length (99) in Bytes (Little Endian format)
03 01 0f: 3 Byte fixed message pattern (supposed `03 00/01 0f` for send/receive)
03 03 : 2 Byte message type pattern (varies per device model and message type)
: 1 Byte optional message increment ( 0)
-> 50 <-: XOR Checksum Byte (last message byte), Crosscheck: 00 => OK
-- Fields --|- Value (Hex/Decode Options)---------------------------------------
Fld Len Type uIntLe/var sIntLe floatLe dblLe/4int
a1 01 -- 34
└-> 1 unk b'4'
a2 08 04 01:30:4d:64:00:c5:00
└-> 8 bin b'\x010Md\x00\xc5\x00' --> {'00': {'name': 'usbc_1_status', 'type': b'\x01'}, '01': {'name': 'usbc_1_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_1_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_1_power', 'type': b'\x02', 'factor': 0.01}}
a3 08 04 01:40:3a:00:00:00:00
└-> 8 bin b'\x01@:\x00\x00\x00\x00' --> {'00': {'name': 'usbc_2_status', 'type': b'\x01'}, '01': {'name': 'usbc_2_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_2_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_2_power', 'type': b'\x02', 'factor': 0.01}}
a4 08 04 01:90:13:00:00:00:00
└-> 8 bin b'\x01\x90\x13\x00\x00\x00\x00' --> {'00': {'name': 'usbc_3_status', 'type': b'\x01'}, '01': {'name': 'usbc_3_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_3_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_3_power', 'type': b'\x02', 'factor': 0.01}}
a5 08 04 01:78:13:00:00:00:00
└-> 8 bin b'\x01x\x13\x00\x00\x00\x00' --> {'00': {'name': 'usbc_4_status', 'type': b'\x01'}, '01': {'name': 'usbc_4_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usbc_4_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usbc_4_power', 'type': b'\x02', 'factor': 0.01}}
a6 08 04 00:00:00:00:00:00:00
└-> 8 bin b'\x00\x00\x00\x00\x00\x00\x00' --> {'00': {'name': 'usba_1_status', 'type': b'\x01'}, '01': {'name': 'usba_1_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usba_1_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usba_1_power', 'type': b'\x02', 'factor': 0.01}}
a7 08 04 00:00:00:00:00:00:00
└-> 8 bin b'\x00\x00\x00\x00\x00\x00\x00' --> {'00': {'name': 'usba_2_status', 'type': b'\x01'}, '01': {'name': 'usba_2_voltage', 'type': b'\x02', 'factor': 0.001}, '03': {'name': 'usba_2_current', 'type': b'\x02', 'factor': 0.001}, '05': {'name': 'usba_2_power', 'type': b'\x02', 'factor': 0.01}}
a8 11 04 00:00:00:00:fa:ff:fb:ff:00:00:00:00:00:00:00:00
└-> 17 bin b'\x00\x00\x00\x00\xfa\xff\xfb\xff\x00\x00\x00\x00\x00\x00\x00\x00' --> {'00': {'name': 'unknown_a8_00_01', 'type': b'\x02'}, '01': {'name': 'unknown_a8_01_02', 'type': b'\x02'}, '02': {'name': 'unknown_a8_02_03', 'type': b'\x02'}, '03': {'name': 'unknown_a8_03', 'type': b'\x01'}}
fe 05 03 32:5a:56:69
└-> 5 var 23090;26966 1767266866 16196003794887037308370944.000 50;90;86;105 --> msg_timestamp (2026-01-01 11:27:46)
-------------------------------------------------------------------------------- |
|
@thomluther good to know, I might end up implementing that in future but at the moment its not easily possible due to the issues of decoding telemetry above a certain size that are split across payloads as the newer prime style devices are a bit wacky in that department (they only sometimes encode information about if a packet is fragmented rather than always with the Solix style), but after that’s sorted out it should be possible. |
|
This PR is working for mine as well. (A2345 firmware 2.1.1.6, for the record) (Edit to add): Although I am seeing sensor updates, I am also seeing log messages that are not raised with the #51 stack, including... |
I am seeing the same thing during the negotiation phase, my guess is its the pesky fragmentation causing issues again, but since that part of the negotiation is not needed to receive telemetry it can be safely ignored for now and with any luck will go away when fragmentation is fully figured out. |
|
I have also been able to add support for controlling the USB outputs and setting timers. The only issues that remain are the incorrect clock/time zone, which will need to be fixed in another PR, the issues with occasionally un-parseable packets, which will also need to be fixed in another PR, and a weird issue where the USB C ports report 0.02A of current with nothing plugged in, which I think is fine for now. |
Issues were found in #51 to do with the Prime 250w charger needing a keep-alive command to be sent every 10~ seconds in order to receive telemetry. I have now obtained this device and have tested it myself and have been able to confirm that it does need this command to be sent, however the other changes to negotiation were not needed for this specific device.
This PR fixes support for this device by adding a new _keep_alive() function to the base SolixBLEDevice class which is called periodically when connected and negotiated, with this automatically stopping when disconnected.
This is currently experimental and I might end up changing things before merging (I am not sure about the interface or if it should be merged with _post_connect), but I am mostly happy with this as a solution.