UPDATE:
I have figured it out !!
I have managed to add buttons that fire the commands successfully!
I’ve made some notes which I share below, but I still might need some help to change the summer mode from recirculate to extract and back (see picture).
Also, in my last tests in the document below, I get weird values from reading the temperatures. They are very high / impossible. At some point in the protocol, it states “* Gesendeter Wert ist (Temperatur + 20) * 2”, but how to calculate?
Values should be below 20 Celsius. Is this a matter of taking HEX value 44, -20 : 2?
Tested and working commands:
Ventilation speed control 1/2/3/auto
Read firmware
Read Values
Missing:
Change Winter Mode (re-circulate) to Summer Mode (Extract air only) (see picture above)
Nice-To-Haves:
Feedback (ACK) to my buttons, like a little greed led bulb that goes green for a second and red when no ACK came back.
Home Assistant:
Home Assistant Terminal & SSH Shell Commands Test:
Open the HA SSH Terminal and verify if nc (NetCat) is installed with this command:
nv --version
If you see the version details, it works
WHR903 / CA350 test commands:
(replace my IP and Port to match the IP of the Elfin / Moxa or other brand device you have used to convert Serial to IP)
Fan 1
echo -e -n '\x07\xF0\x00\x99\x01\x04\x4B\x07\x0F' | nc 192.168.178.22 8899
Fan 3
echo -e -n '\x07\xF0\x00\x99\x01\x02\x49\x07\x0F' | nc 192.168.178.22 8899
Firmware (no response):
echo -e -n '\x07\xF0\x00\x69\x00\x16\x07\x0F' | nc 192.168.178.22 8899
Script to add in configurations.yaml:
shell_command:
run_nc_command_fan_1: echo -e -n '\x07\xF0\x00\x99\x01\x02\x49\x07\x0F' | nc 192.168.178.22 8899
run_nc_command_fan_2: echo -e -n '\x07\xF0\x00\x99\x01\x03\x4A\x07\x0F' | nc 192.168.178.22 8899
run_nc_command_fan_3: echo -e -n '\x07\xF0\x00\x99\x01\x04\x4B\x07\x0F' | nc 192.168.178.22 8899
Button in Lovelace to call script:
show_name: true
show_icon: true
type: button
name: Send Hex Command Fan 1
icon: mdi:fan-speed-1
tap_action:
action: perform-action
perform_action: shell_command.run_nc_command_fan_1
Notepad:
Storkair WHR930 RS232 via Extron DataViewer
TX firmware version:
$07$F0$00$69$00$16$07$0F (Hercules format)
%07%F0%00%69%00%16%07%0F (Extron DataViewer format)
\x07\xF0\x00\x69\x00\x16\x07\x0F (Home Assistant Format)
RX Hercules format:
{07}ð{00}i{00}{16}{07}{0F}{07}ó{07}ð{00}j{0D}{03}F CA350 luxe‡{07}{0F}
RX DataViewer format in both HEX and ASSCII:
[07][F3][07][F0][00][6A][0D][03][46][20][43][41][33][35][30][20][6C][75][78][65][87][07][0F]
ó ðj
F CA350 luxe‡
Calculating the checksum for example command %07%F0%00%69%00%16%07%0F in Decimals:
Beispiel:
Kommando: 0x00 0x69
Anzahl: 0x00
Summe = 0 + 105 + 0 + 173 = 278
278 = 0x0116
Checksumme = 0x16
Calculating the checksum via MS Windows 11 Calculator (put on Programmer mode) in HEX field:
Beispiel:
Kommando: 0x00 0x69
Anzahl: 0x00
Summe = 0 + 69 + 0 + AD =
116 = 0x0116
Checksumme = 0x16
*Die Checksumme ergibt sich durch Addition aller Bytes (exklusive Start und Ende) plus 173. *Tauch der Wert 0x07 doppelt im Datenbereich auf, so wird nur eine 0x07 für die *Checksummenberechnung benutzt.
*Wenn die Checksumme größer als ein Byte ist, wird das niederwertigste Byte verwendet.
From here, I will use Extron DataViewer format.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX ventilatorstatus abrufen:
Kommando: 0x00 0x0B Ventilatorstatus abrufen
Daten: -
Antwort: 0x00 0x0C
Daten: Byte[1]
Byte[2]
Byte[3-4]
Byte[5-6]
Zuluft (%)
Abluft (%)
Drehzahl Zuluft Ventilator (U/Min**)
Drehzahl Abluft Ventilator (U/Min**)
Command:
%07%F0%00%0B (00 0B is the command)
Calculating the checksum in Decimals:
00+11+00+173 (HEX 0B = Decimal 11)
Calculating the checksum in HEX:
00+0B+00+AD (HEX AD = Decimal 173 which is the value we need to add according tot the RS232 Protocol)
Command with checksum:
%07%F0%00%0B%00%B8%07%0F
RX Feedback:
[07][F3][07][F0][00][0C][06][5A][64][02][B8][02][80][B9][07][0F]
RX Feedback Explained:
[07][F3] ack
[07][F0] start
[00][0C] command
[06] data length
[5A][64][02][B8][02][80] data (e.g. hex 64 = decimal 100)
[B9] checksum
[07][0F] end
TX Ventstatus abfruffen:
%07%F0%00%CD (CD = decimal 205 + checksum 173 = decimal 378 = hex 17A)
%07%F0%00%CD%00%7A%07%0F (17A becomes hex 7A, skipp the 1 in 17A or any HEX value starting with 1)
RX:
[07][F3] ack
[07][F0] start
[00][CE] command (Start of the checksum = 00 + CE = 206 bytes in Decimals)
[0E] length (= 14 bytes)
[0F] byte 1 (= 15 bytes)
[1E] byte 2 (= 30 bytes)
[3C] byte 3 (= 60 bytes)
[0F] byte 4 (= 15)
[19] byte 5 (= 25)
[32] byte 6 (= 50)
[64] byte 7 (= 100)
[5A] byte 8 (= 90)
[04] byte 9 (= 4)
[01] byte 10 (= 1)
[64] byte 11 (= 100)
[5A] byte 12 (= 90)
[00] byte 13 (= 00)
[00] byte 14 (End of the checksum = 00 + Decimal 173 which is HEX AD)
[CD] checksum (see below for the calculation that matches this value)
[07][0F] end
Calculating the Checksum:
00+CE+0E+0F+1E+3C+0F+19+32+64+5A+04+01+64+5A+00+00+AD = hex 3CD (see above to see this is correct, we ignore the first digit)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Random test commands:
TX Klappenstatus abrufen :
%07%F0%00%0D (0D + AD (173) = BA)
%07%F0%00%0D%00%BA%07%0F
RX:
[07][F3][07][F0][00][0E][04][00][FF][00][00][BE][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][0E] Command
[04] RX has 4 Bytes of data
[00] Byte 1 = FF = Bypass (%) (0xFF = undefiniert)
[FF] Byte 2 = 00 = Vorheizung (1 = Offen / 0 = Zu / 2 = Unbekannt)
[00] Byte 3 = 00 = Bypass Motorstrom (ADC Rohdaten)
[00] Byte 4 = 190 = Vorheizung Motorstrom (ADC Rohdaten)
[BE] Checksum (00 + 0E + 04 + 00 + FF + 00 + 00 = BE)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Stufe setzen Niedrig:
%07%F0%00%99
%07%F0%00%99%01%02%49%07%0F (99+1+2+AD = 149 (write 49)
TX Stufe setzen Mittel:
%07%F0%00%99
%07%F0%00%99%01%03%4B%07%0F (99+1+3+AD = 14A (write 4A)
TX Stufe setzen Hogh:
%07%F0%00%99
%07%F0%00%99%01%04%4B%07%0F (99+1+4+AD = 14B (write 4B)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX RS232 Modus Command info:
Kommando:
0x00 0x9B Command RS232 Modus setzen
Daten : Byte[1]
0x00 = Ende
0x01 = Nur PC
0x03 = PC Master
0x04 = PC Logmodus
TX RS232 Modus setsen:
%07%F0%00%9B
%07%F0%00%9B%01%04%4D%07%0F (9B + 01 + 04 + AD = 14D becomes 4D)
RX:
ó ðœK Response
[07][F3][07][F0][00][9C][01][01][4B][07][0F] Response in HEX
RX Antwort info:
0x00 0x9C
Daten: Byte[1] (0x00 = Ohne Verbindung)
0x01 = Nur PC
0x02 = Nur CC-Ease
0x03 = PC Master
0x04 = PC Logmodus
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][9C] Command
[01] Verbindung (00 = Ohne Verbindung, 01 = Verbindung)
[01] Verbindung Typer (0x01 = Nur PC, 0x02 = Nur CC-Ease, 0x03 = PC Master, 0x04 = PC Logmodus)
[4B] Calculated Checksum
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Konnektorplatine Version abrufen:
%07%F0%00%A1
%07%F0%00%A1%00%4E%07%0F (A1 + 00 + AD = 14E becomes 4E)
RX:
ó ð¢ÿÿÿÿÿÿÿÿõe¯
[07][F3][07][F0][00][A2][0E][FF][FF][FF][FF][00][FF][FF][FF][FF][F5][00][65][00][00][AF][07][0F]
RX Explained:
[07][F3]Ack
[07][F0]Start
[00][A2]Command
[0E] Version Major
[FF] Byte 1 (Version Major)
[FF] Byte 2 (Version Minor)
[FF] Byte 3 (Gerätename (ASCII String)
[FF] Byte 4 (Gerätename (ASCII String)
[00] Byte 5 (Gerätename (ASCII String)
[FF] Byte 6 (Gerätename (ASCII String)
[FF] Byte 7 (Gerätename (ASCII String)
[FF] Byte 8 (Gerätename (ASCII String)
[FF] Byte 9 (Gerätename (ASCII String)
[F5] Byte 10 (Gerätename (ASCII String)
[00] Byte 11 (Gerätename (ASCII String)
[65] Byte 12 (Gerätename (ASCII String)
[00] Byte 13 (Version CC-Ease Bit 7..4 = Version Major Bit 3..0 = Version Minor)
[00] Byte 14 (Version CC-Luxe Bit 7..4 = Version Major Bit 3..0 = Version Minor)
[AF] Checksum
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Temperaturstatus abrufen Info:
Kommando: 0x00 0x0F Temperaturstatus abrufen
Daten: -
Antwort: 0x00 0x10
Daten:
Byte[1]
Byte[2]
Byte[3]
Byte[4]
T1 / Außenluft (°C*)
T2 / Zuluft (°C*)
T3 / Abluft (°C*)
T4 / Fortluft (°C*)
* Gesendeter Wert ist (Temperatur + 20) * 2
TX Temperaturstatus:
%07%F0%00%0F
%07%F0%00%0F%00%BC%07%0F (OF + AD = BC) (Decimal - 15 + 173 = 188)
RX:
ó ðDKJEß
[07][F3][07][F0][00][10][04][44][4B][4A][45][DF][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][10] Antwort
[04] Number of Bytes
[44] T1 68°C
[4B] T2 75°C
[4A] T3 74°C
[45] T4 69°C
[DF] Checksum
[07][0F] End
Storkair WHR930 RS232 via Extron DataViewer
TX firmware version:
$07$F0$00$69$00$16$07$0F (Hercules format)
%07%F0%00%69%00%16%07%0F (Extron DataViewer format)
RX Hercules format:
{07}ð{00}i{00}{16}{07}{0F}{07}ó{07}ð{00}j{0D}{03}F CA350 luxe‡{07}{0F}
RX DataViewer format in both HEX and ASSCII:
[07][F3][07][F0][00][6A][0D][03][46][20][43][41][33][35][30][20][6C][75][78][65][87][07][0F]
ó ðj
F CA350 luxe‡
Calculating the checksum for example command %07%F0%00%69%00%16%07%0F in Decimals:
Beispiel:
Kommando: 0x00 0x69
Anzahl: 0x00
Summe = 0 + 105 + 0 + 173 = 278
278 = 0x0116
Checksumme = 0x16
Calculating the checksum via MS Windows 11 Calculator (put on Programmer mode) in HEX field:
Beispiel:
Kommando: 0x00 0x69
Anzahl: 0x00
Summe = 0 + 69 + 0 + AD =
116 = 0x0116
Checksumme = 0x16
*Die Checksumme ergibt sich durch Addition aller Bytes (exklusive Start und Ende) plus 173. *Tauch der Wert 0x07 doppelt im Datenbereich auf, so wird nur eine 0x07 für die *Checksummenberechnung benutzt.
*Wenn die Checksumme größer als ein Byte ist, wird das niederwertigste Byte verwendet.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
From here, I will use Extron DataViewer format.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX ventilatorstatus abrufen:
Kommando: 0x00 0x0B Ventilatorstatus abrufen
Daten: -
Antwort: 0x00 0x0C
Daten: Byte[1]
Byte[2]
Byte[3-4]
Byte[5-6]
Zuluft (%)
Abluft (%)
Drehzahl Zuluft Ventilator (U/Min**)
Drehzahl Abluft Ventilator (U/Min**)
Command:
%07%F0%00%0B (00 0B is the command)
Calculating the checksum in Decimals:
00+11+00+173 (HEX 0B = Decimal 11)
Calculating the checksum in HEX:
00+0B+00+AD (HEX AD = Decimal 173 which is the value we need to add according tot the RS232 Protocol)
Command with checksum:
%07%F0%00%0B%00%B8%07%0F
RX Feedback:
[07][F3][07][F0][00][0C][06][5A][64][02][B8][02][80][B9][07][0F]
RX Feedback Explained:
[07][F3] ack
[07][F0] start
[00][0C] command
[06] data length
[5A][64][02][B8][02][80] data (e.g. hex 64 = decimal 100)
[B9] checksum
[07][0F] end
TX Ventstatus abfruffen:
%07%F0%00%CD (CD = decimal 205 + checksum 173 = decimal 378 = hex 17A)
%07%F0%00%CD%00%7A%07%0F (17A becomes hex 7A, skipp the 1 in 17A or any HEX value starting with 1)
RX:
[07][F3] ack
[07][F0] start
[00][CE] command (Start of the checksum = 00 + CE = 206 bytes in Decimals)
[0E] length (= 14 bytes)
[0F] byte 1 (= 15 bytes)
[1E] byte 2 (= 30 bytes)
[3C] byte 3 (= 60 bytes)
[0F] byte 4 (= 15)
[19] byte 5 (= 25)
[32] byte 6 (= 50)
[64] byte 7 (= 100)
[5A] byte 8 (= 90)
[04] byte 9 (= 4)
[01] byte 10 (= 1)
[64] byte 11 (= 100)
[5A] byte 12 (= 90)
[00] byte 13 (= 00)
[00] byte 14 (End of the checksum = 00 + Decimal 173 which is HEX AD)
[CD] checksum (see below for the calculation that matches this value)
[07][0F] end
Calculating the Checksum:
00+CE+0E+0F+1E+3C+0F+19+32+64+5A+04+01+64+5A+00+00+AD = hex 3CD (see above to see this is correct, we ignore the first digit)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Random test commands:
TX Klappenstatus abrufen :
%07%F0%00%0D (0D + AD (173) = BA)
%07%F0%00%0D%00%BA%07%0F
RX:
[07][F3][07][F0][00][0E][04][00][FF][00][00][BE][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][0E] Command
[04] RX has 4 Bytes of data
[00] Byte 1 = FF = Bypass (%) (0xFF = undefiniert)
[FF] Byte 2 = 00 = Vorheizung (1 = Offen / 0 = Zu / 2 = Unbekannt)
[00] Byte 3 = 00 = Bypass Motorstrom (ADC Rohdaten)
[00] Byte 4 = 190 = Vorheizung Motorstrom (ADC Rohdaten)
[BE] Checksum (00 + 0E + 04 + 00 + FF + 00 + 00 = BE)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Stufe setzen Niedrig:
%07%F0%00%99
%07%F0%00%99%01%02%49%07%0F (99+1+2+AD = 149 (write 49)
TX Stufe setzen Mittel:
%07%F0%00%99
%07%F0%00%99%01%03%4A%07%0F (99+1+3+AD = 14A (write 4A)
TX Stufe setzen Hogh:
%07%F0%00%99
%07%F0%00%99%01%04%4B%07%0F (99+1+4+AD = 14B (write 4B)
\x07\xF0\x00\x99\x01\x04\x4B\x07\x0F (Home Assistant nc shell command format)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX RS232 Modus Command info:
Kommando:
0x00 0x9B Command RS232 Modus setzen
Daten : Byte[1]
0x00 = Ende
0x01 = Nur PC
0x03 = PC Master
0x04 = PC Logmodus
TX RS232 Modus setsen:
%07%F0%00%9B
%07%F0%00%9B%01%04%4D%07%0F (9B + 01 + 04 + AD = 14D becomes 4D)
RX:
ó ðœK Response
[07][F3][07][F0][00][9C][01][01][4B][07][0F] Response in HEX
RX Antwort info:
0x00 0x9C
Daten: Byte[1] (0x00 = Ohne Verbindung)
0x01 = Nur PC
0x02 = Nur CC-Ease
0x03 = PC Master
0x04 = PC Logmodus
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][9C] Command
[01] Verbindung (00 = Ohne Verbindung, 01 = Verbindung)
[01] Verbindung Typer (0x01 = Nur PC, 0x02 = Nur CC-Ease, 0x03 = PC Master, 0x04 = PC Logmodus)
[4B] Calculated Checksum
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Konnektorplatine Version abrufen:
%07%F0%00%A1
%07%F0%00%A1%00%4E%07%0F (A1 + 00 + AD = 14E becomes 4E)
RX:
ó ð¢ÿÿÿÿÿÿÿÿõe¯
[07][F3][07][F0][00][A2][0E][FF][FF][FF][FF][00][FF][FF][FF][FF][F5][00][65][00][00][AF][07][0F]
RX Explained:
[07][F3]Ack
[07][F0]Start
[00][A2]Command
[0E] Version Major
[FF] Byte 1 (Version Major)
[FF] Byte 2 (Version Minor)
[FF] Byte 3 (Gerätename (ASCII String)
[FF] Byte 4 (Gerätename (ASCII String)
[00] Byte 5 (Gerätename (ASCII String)
[FF] Byte 6 (Gerätename (ASCII String)
[FF] Byte 7 (Gerätename (ASCII String)
[FF] Byte 8 (Gerätename (ASCII String)
[FF] Byte 9 (Gerätename (ASCII String)
[F5] Byte 10 (Gerätename (ASCII String)
[00] Byte 11 (Gerätename (ASCII String)
[65] Byte 12 (Gerätename (ASCII String)
[00] Byte 13 (Version CC-Ease Bit 7..4 = Version Major Bit 3..0 = Version Minor)
[00] Byte 14 (Version CC-Luxe Bit 7..4 = Version Major Bit 3..0 = Version Minor)
[AF] Checksum
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Temperaturstatus abrufen Info:
Kommando: 0x00 0x0F Temperaturstatus abrufen
Daten: -
Antwort: 0x00 0x10
Daten:
Byte[1]
Byte[2]
Byte[3]
Byte[4]
T1 / Außenluft (°C*)
T2 / Zuluft (°C*)
T3 / Abluft (°C*)
T4 / Fortluft (°C*)
* Gesendeter Wert ist (Temperatur + 20) * 2
TX Temperaturstatus:
%07%F0%00%0F
%07%F0%00%0F%00%BC%07%0F (OF + AD = BC) (Decimal - 15 + 173 = 188)
RX:
ó ðDKJEß
[07][F3][07][F0][00][10][04][44][4B][4A][45][DF][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][10] Antwort
[04] Number of Bytes
[44] T1 68°C
[4B] T2 75°C
[4A] T3 74°C
[45] T4 69°C
[DF] Checksum
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Eingänge abrufen Info:
Kommando: 0x00 0x03 Eingänge abrufen
Daten: -
Antwort: 0x00 0x04
Daten: Byte[1]
Byte[2]
Stufenschalter: (1 = aktiv / 0 = inaktiv)
0x01 = L1
0x02 = L2
Schalteingänge: (1 = aktiv / 0 = inaktiv)
0x01 = Badezimmerschalter
0x02 = Küchenhaube Schalter
0x04 = Externer Filter
0x08 = Wärmerückgewinnung (WTW)
TX Eingänge abrufen:
%07%F0%00%03
%07%F0%00%03%00%B0%07%0F (03 + 00 + AD = B0)
RX:
[07][F3][07][F0][00][04][02][00][00][B3][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][04] Command
[02] Number of Bytes
[00] Byte 1 = 00 (Stufenschalter: 1 = aktiv / 0 = inaktiv)
[00] Byte 2 = 00 (Schalteingänge: (1 = aktiv / 0 = inaktiv)
[B3] Checksum (00 + 04 + 02 + 00 + 00 + AD = B3)
[07][0F] End
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
TX Ventilatorstatus abrufen Info:
Kommando: 0x00 0x0B Ventilatorstatus abrufen
Daten: -
Antwort: 0x00 0x0C
Daten: Byte[1]
Byte[2]
Byte[3-4]
Byte[5-6]
Zuluft (%)
Abluft (%)
Drehzahl Zuluft Ventilator (U/Min**)
Drehzahl Abluft Ventilator (U/Min**)
TX Ventilatorstatus abrufen:
%07%F0%00%0B
%07%F0%00%0B%00%B8%07%0F (0B + 00 + AD = B8)
RX:
[07][F3][07][F0][00][0C][06][19][1E][08][2C][08][78][AA][07][0F]
RX Explained:
[07][F3] Ack
[07][F0] Start
[00][0C] Command
[06] Number of Bytes
[19] Byte 1 =
[1E] Byte 2 =
[08] Byte 3 =
[2C] Byte 4 =
[08] Byte 5 =
[78] Byte 6 =
[AA] Checksum (00 + 0C + 06 + 19 + 1E + 08 + 2C + 08 + 78 + AD = 1AA so read AA)
[07][0F]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>