revk / ESP32-Faikin

ESP32 based module to control Daikin aircon units
GNU General Public License v3.0
462 stars 69 forks source link

[Documentation] S21 protocol: new findings #408

Open misterobotique opened 3 months ago

misterobotique commented 3 months ago

Long story short, I've also been reverse engineering S21 protocol and here are some of my findings.

Hardware

  1. air conditioner: S22ZTES-W (interior unit), F22ZTES-W (exterior unit)
  2. interface adaptor for wired remote controller: KRP067A41
  3. wired remote control: KRC081A1

Communication method

UART, 2400bps, 8 bit data, 2 bit stop, even parity, DC 5V voltage level.

Request commands

1) Message format

STX Byte(0) Byte(1) Checksum ETX Checksum is the Modulo256 of the sum of Byte(0) and Byte(1).

2) Command list

Here is a list of what might be new commands. Byte(0) Byte(1) Description
F 3 Timer
F 9 Coarse indoor, outdoor temperature (℃)
F M Total energy consumption (kWh)
R g Compressor status (ON/OFF)
R A Power status (ON/OFF)
R B Operation mode
R D ON Timer setting
R E OFF Timer setting
R F Swing setting
R K Cross flow fan speed setpoint (rpm)
R L Cross flow fan speed (rpm)
R M Louver vertical angle setpoint (degree)
R N Louver vertical angle (degree)

3) Unknown command list

Here is a list of commands my air conditioner responds to but I have not yet identified. (Result of testing the commands F0 to F9, FA to FZ, R0 to R9, Ra to Rz and RA to RZ.) Byte(0) Byte(1) Response
F 2 0x34 0x3A 0x80 x080
F 4 0x30 0x00 0x80 0x00
F 8 0x30 0x32 0x00 0x00
F A FFFF
F B 0000
F C 1A01
F G 0801 (see below)
F K 1001
F L 0000
F N 0000
F P 8300
F Q F200
F R ?000
F S 0000
F T 1000
R b 300
R e 050
R z 0x53 0x7A
R C 720+
R G A
R W 00

Details: [ . ][X][ . ][ . ] increments by one (from 0 to F and repeat) each time the air conditioner receives a command from the remote control.

2) Request sequence

The wired remote control sends the following sequence of commands indefinitely. D20 -> F8 -> F2 -> F4 -> F3 -> F1 -> F5 -> D80000 -> RH -> F8 -> F2 -> F4 -> F3 -> F1 -> F5 -> D80000 -> Ra I haven't figured out D20 neither D80000 yet. Response to D20 is always ACK while D80000 is always NAK. Upon receipt of a response, the remote control sends an ACK after about 45ms. The time between the remote control sending an ACK and then sending a new command is about 36ms.

Response

1) Message format

ACK STX Byte(0) Byte(1) ... Byte(n) Checksum ETX Checksum is the Modulo256 of the sum of Byte(0) to Byte(n)

2) Timer

Bytes Values Description
Byte(0) G -
Byte(1) 3 -
Byte(2) 0, 1, 2, 3 Timers disabled, ON Timer, OFF Timer, Both timers
Byte(3) 0x36~0x78 ON Timer (1~12 hours)
Byte(4) 0x36~0x78 OFF Timer (1~12 hours)
Byte(5) 0 -

Timer setting (hours) = (Value – 0x30) / 6.

3) Coarse indoor, outdoor temperature (℃)

Bytes Values Description
Byte(0) G -
Byte(1) 9 -
Byte(2) see below Coarse indoor temperature
Byte(3) see below Coarse outdoor temperature
Byte(4) 0xFF -
Byte(5) 0x30 -

Temperature = (Value – 0x80) / 2.

4) Total energy consumption (kWh)

Bytes Values Description
Byte(0) G -
Byte(1) M -
Byte(2) 0~F Data[3]
Byte(3) 0~F Data[2]
Byte(4) 0~F Data[1]
Byte(5) 0~F Data[0]

Total energy consumption = Data[0~3] → Decimal / 10.

5) Compressor status (ON/OFF)

Bytes Values Description
Byte(0) S -
Byte(1) g -
Byte(2) 0, 1 Compressor OFF, Compressor ON

6) Power status (ON/OFF)

Bytes Values Description
Byte(0) S -
Byte(1) A -
Byte(2) 0, 1 Power OFF, Power ON

7) Operation mode

Bytes Values Description
Byte(0) S -
Byte(1) B -
Byte(2) 0, 2, 3, 4, 6 Auto, Dry, Cooling, Heating, Fan only

8) ON Timer setting

Bytes Values Description
Byte(0) S -
Byte(1) D -
Byte(2) 0~9 Minutes tens
Byte(3) 0~9 Minutes hundreds
Byte(4) 0~1 Minutes thousands

9) OFF Timer setting

Bytes Values Description
Byte(0) S -
Byte(1) E -
Byte(2) 0~9 Minutes tens
Byte(3) 0~9 Minutes hundreds
Byte(4) 0~1 Minutes thousands

10) Swing setting

Bytes Values Description
Byte(0) S -
Byte(1) F -
Byte(2) 0, 1, 2, 7 Disabled, Up & down, Left & right, Both
Byte(3) 0, F -

Byte(3) is 0 when Byte(2) = Disabled, F otherwise.

11) Cross flow fan speed setpoint (rpm)

Bytes Values Description
Byte(0) S -
Byte(1) K -
Byte(2) 0~9 Speed tens
Byte(3) 0~9 Speed hundreds
Byte(4) 0~1 Speed thousands

12) Cross flow fan speed (rpm)

Bytes Values Description
Byte(0) S -
Byte(1) L -
Byte(2) 0~9 Speed tens
Byte(3) 0~9 Speed hundreds
Byte(4) 0~1 Speed thousands

13) Louver vertical angle setpoint (degree)

Bytes Values Description
Byte(0) S -
Byte(1) M -
Byte(2) 0~9 Angle units
Byte(3) 0~9 Angle tens
Byte(4) 0~1 Angle hundreds
Byte(4) +, - Angle sign

When the louver is in the closed position, the vertical angle is 120°.

14) Louver vertical angle (degree)

Bytes Values Description
Byte(0) S -
Byte(1) N -
Byte(2) 0~9 Angle units
Byte(3) 0~9 Angle tens
Byte(4) 0~1 Angle hundreds
Byte(4) +, - Angle sign

Hope this help.

revk commented 3 months ago

Wow!!!

Ok we have most of that, but there are a couple of ones we can add.

MassiPi commented 3 months ago

this is a wonderful work, but i wonder why i use mostyle different commands 🤣 being serious, i assume there are different S21 protocol versions. For example, i have F1 that gives me all basic info (mode, fan, power), you have not. F5 for me is swing state, you have not.

however, some of these commands get an answer also on my unit. Thanks! just wondering why there is the info of target and real angle of VERTICAL louver, but not HORIZONTAL

have you tried also SETTING values? (i assume via D commands)? would be nice to set target position of louver :) will it be DK?

misterobotique commented 3 months ago
I've only posted the commands that I believe are not fully documented or commands that are still unknown. The commands you mentioned including the following work with my air conditioner: Byte(0) Byte(1) Description
F 1 Power (ON/OFF), operation mode, temperature setpoint (℃), wind speed
F 5 Swing setting
R a Outdoor temperature (℃)
R H Indoor temperature (℃)
R I Refrigerant temperature (at the evaporator coil inlet) (℃)
R X Temperature setpoint (℃)
For the temperature setpoint of F1, when operation mode = dry the temperature is how much the indoor temperature at start of dry operation is allowed to be warmed up/cooled down while removing moisture (see table below). Value Description Remark
0x82 +2℃ Low dehumidification effect
0x81 +0.5~1.5℃
0x80 0℃
0x8F -0.5 ~ -1.5℃
0x8E -2℃ High dehumidification effect

My air conditioner model can only move the louver up and down, so I wasn't able to find which command can fetch the louver horizontal angle setpoint and actual one (if existing).

I've just started to play with commands for setting values and so far I've confirmed D1 and D5 work.

MassiPi commented 3 months ago

ok this makes sense :) thanks a lot, i'm having some fun.. the only issue i see is that the more commands we send, the longer it takes for update, so we need to lower the refresh frequency

don't you have Rd working?

misterobotique commented 3 months ago

Yes, Rd is also working (forget to add it in the previous table of known and working commands).

MassiPi commented 3 months ago

any idea on the meaning of Rd response? mine is commonly 20 or 40, i see around talking about frequency but to me it seems more a percentage of load..

misterobotique commented 3 months ago

Regarding Rd response, I would agree with the idea that it is the inverter compressor frequency (Hz). According to ChatGPT, the normal operating frequency range for the compressor in consumer-oriented air conditioners is around 20-30 Hz for minimal cooling, 40-80 Hz for moderate cooling and 90-120 Hz for maximum cooling. In my case, with a temperature setpoint of 27℃ I've seen values like 26Hz in the morning when it's around 30℃ outside and around 65Hz in the afternoon when it's above 37℃ outside.

For so, I've defined Rd response as follow: Byte Description Value
Byte(0) - S
Byte(1) - d
Byte(2) Frequency units
Byte(3) Frequency tens
Byte(4) Frequency hundreds

But I wasn't able to confirm it's indeed the compressor frequency as the wired remote control I use for reverse engineering doesn't display neither poll this information from the air conditioner.

MassiPi commented 3 months ago

i can't find official values in the tech document of my external unit, but it makes sense to be frequency, i'll use it in that way :)

MassiPi commented 3 months ago

Ok on my system (old Perfera) i do not have additional responses than yours (so i assume i can't set horizzontal lid angle), but i have a lot of NAK where you get info. On R commands i only miss RW, but on F i have much more holes (F9 | FA | FB | FC | FG | FK | FL | FM | FN | FP | FQ | FR | FS | FT). I assume it's a matter of unit maturity and features..

MassiPi commented 3 months ago

sadly i get NAK for both DK and DM commands, so probably target fan speed and target angle can't be set :(

Sonic-Amiga commented 3 months ago

Hello guys!

I have recently ordered an old stock BRP069B41 online controller specifically for dissection, and after some fighting with geo-blocking i even managed to set it up to work with an A/C simulator, However, it currently sits in "ret=SERIAL IF FAILURE,err=252" state. I am assuming that this means "incompatible A/C model", because communication actually works fine. And if i stop the simulator, error code changes to 251 (and it stops responding to /aircon/* completely, only to /common/basic_info), so the A/C is indeed online.

It currently sends 4 queries in a loop:

Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')

I am thinking that F4 and F2 are responsible for A/C model identification. I hardcoded replies, which my FTXF20D sends. I was planning to play with these values and see how the controller reacts to changes. But first i need to get it fully running.

So hence my first request for help. Can any of you from EU share me replies of your A/C to F2 and F4 commands ? Especially wanted are replies from any model on this list: FTXM-M CTXM-M ATXM-M FTXTM-M BRP069A45 FTXG-LS FTXG-LW FTXJ-MW FTXJ-MS

These are listed on https://www.daikin.eu/en_us/product-group/control-systems/onecta/connectable-units.html as compatible with my parrticular box.

Please reply with data and your A/C model. Just in case if someone cares: these data can't be used to identify you personally.

Another interesting finding is 'FU' command. The controller sends it only once upon startup. My A/C doesn't know it, i checked. So, simulator currently replies with NAK, just like a real unit. I tried to modify the sim to reply with 4 zeroes. Controller status didn't change, but interestingly it responds with a byte of 0x15 instead of ACK before proceeding.

If anyone is interested, you can find my modified simulator here https://github.com/Sonic-Amiga/ESP8266-Faikin/tree/main/Tools/Simulators . I will upstream changes, of course, once i have some more interesting findings.

Sonic-Amiga commented 3 months ago

UPD: The controller appeared fully working with my another unit, ATX20K2V1B. The unit is older and runs BRP069A41; it was available here back then... Dam, that's an ealier revision of the same unit!

Need to Faikin-ize it to shake some info out of it.

hedgepigdaniel commented 3 months ago
Model F2 response F4 response
CTXM60RVMA, CTXM35RVMA 0247323D3B00807103 024734300080305B03
CTXM25RVMA 0247323D3B00807103 0247343000A0307B03

@Sonic-Amiga what responses do your units send?

Sonic-Amiga commented 3 months ago

Snip from the simulator, containing the answer:

         case '2':
            // BRP069B41 sends this as first command. If NAK is received, it keeps retrying
            // and doesn't send anything else. Suggestion - query AC features
            if (debug)
               printf(" -> unknown ('F2')\n");
            response[3] = 0x34; // No idea what this is, taken from my FTXF20D
            response[4] = 0x3A;
            response[5] = 0x00;
            response[6] = 0x80;

            s21_reply(p, response, buf, S21_PAYLOAD_LEN);
            break;
         case '3':
            if (debug)
               printf(" -> powerful ('F3') %d\n", powerful);
            response[3] = 0x30; // No idea what this is, taken from my FTXF20D
            response[4] = 0xFE;
            response[5] = 0xFE;
            response[6] = powerful ? 2 : 0;

            s21_reply(p, response, buf, S21_PAYLOAD_LEN);
            break;
         case '4':
            if (debug)
               printf(" -> unknown ('F4')\n");
            response[3] = 0x30; // No idea what this is, taken from my FTXF20D
            response[4] = 0x00;
            response[5] = 0xA0;
            response[6] = 0x30;

            s21_reply(p, response, buf, S21_PAYLOAD_LEN);
            break;

As to my older ATX20 unit, i have no idea; and i am waiting for my connectors order to arrive so that i could make an adapter for some of ESP hardware i have on hands. So, it doesn't have Faikin currently, so i can't actively research it. Hope parts to arrive next week.

Sonic-Amiga commented 3 months ago

Thank you very much, we're moving. I started changing replies one by one. First i changed F4, the result was the same, error 252. Then i also changed F2, and controller moved on:

$ ./faikin-s21.exe -p COM3 -v
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: F8
 -> Unknown command, sending NAK
Got command: F8
 -> Unknown command, sending NAK

When it gets NAK, it resends the command in an infinite loop. So now i need to implement F8 and probably more. Stay tuned! BTW, you may also order some experiments with my BRP. I wonder if i could share it online...

Sonic-Amiga commented 3 months ago

Wooohooo, off we go, it's alive! Status OK, control works! The code pushed to my repo.

Sonic-Amiga commented 3 months ago

So, very interesting. This is the log when it worked:

$ ./faikin-s21.exe -p COM3 -v
Got command: F8
 -> unknown ('F8')
Got command: F9
 -> Unknown command, sending NAK
Got command: F6
 -> powerful ('F6') 0
Got command: F7
 -> eco 0
Got command: FB
 -> Unknown command, sending NAK
Got command: FG
 -> Unknown command, sending NAK
Got command: FK
 -> Unknown command, sending NAK
Got command: FM
 -> Unknown command, sending NAK
Got command: FN
 -> Unknown command, sending NAK
Got command: FP
 -> Unknown command, sending NAK
Got command: FQ
 -> Unknown command, sending NAK
Got command: FS
 -> Unknown command, sending NAK
Got command: FT
 -> Unknown command, sending NAK
Got command: Rd
 -> Unknown command, sending NAK
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: RL
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: RH
 -> 'H' sensor = +245
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: RN
 -> Unknown command, sending NAK
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: RI
 -> 'I' sensor = +185
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: Ra
 -> 'a' sensor = +205
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: RX
 -> Unknown command, sending NAK
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5
 -> swing 0
Got command: F2
 -> unknown ('F2')
Got command: F1
 -> power 0 mode 3 temp 22.5
Got command: F3
 -> powerful ('F3') 0
Got command: F4
 -> unknown ('F4')
Got command: F5

So, it looped: F2 F1 F3 F4 F5; and every iteration it also queried one sensor ('R'). Majority of them aren't implemented in the simulator yet.

But i am unable to reproduce this. After i restarted the sim and controller, after F8 it started wanting F9. Then FB. Something is off; perhaps i unintentionally exploited some bug.

Sonic-Amiga commented 3 months ago

I reproduced my success following the algorithm: Every time we get F8, increment a counter. Drop it 15 times (do not respond), then reset the counter to zero and say NAK Eventually the controller gives up probing and goes online (see log above)

F8 response from my faikin-ized unit is 30 32 30 30, i. e. '0020' (we read it in reverse like other values) If i implement this response, the controller does not calm down and wants F9, then tons of other stuff (almost all english alphabet), then proceeds to 'Mx`, where x is more characters. I gave up on that, too many commands. Interestingly enough, my FTX20 knows all of them.

I have also just tried 30 30 30 30, and also 00 00 00 00. Different thing happens; the controller now wants MM

Conclusion: F8 is used to query for additional features.

Sonic-Amiga commented 3 months ago

Further confirmation to the theory is the log:

Got command: F8
 -> unknown ('F8')
Got command: RH
 -> 'H' sensor = +245
Got command: Ra
 -> 'a' sensor = +205

So, it does not go for F9 (which, we know, reports home and outside temp in some 'new' form), but instead asks for RH and Ra. Protocol version maybe ?

Sonic-Amiga commented 3 months ago

Confirmed. F8 = "protocol version". Known responses are '0020' and '0010'. '0000' does the same as '0010'; i guess they do "if (version > '1')" sort of check.

Version 1 has interesting 'MM' command, it replies with 'MFFFF' (yes, just one command byte, 5 bytes total). No idea what that is, grabbed from my FTXF20. I tried different second chars (MM, MN, MA), they all seem to produce an identical response.

Hack with timeouting removed, the controller is online with simulated A/C.

Funny that Faikin mis-detected 'M' response as loopback, i'll fix it.

misterobotique commented 3 months ago

With my Japanese model, the command F8 returns: G 8 0 2 0x00 0x00. Command F2 returns: G 2 4 : 0x00 0x80. Command F4 returns: G 4 0 0x00 0xA0 the first time then always G 4 0 0x00 0x80

I've also tried to send different commands, like "A" or "HG" or "cd" and so on but only got responses from the following: Command Response
A STX C A ETX
B ACK
G 0xC5
M STX M F F F F e ETX
V STX V 0 0 A 0 ' ETX
Sonic-Amiga commented 3 months ago

@hedgepigdaniel So, i am currently simulating CTXM60RVMA (not exactly, however) . I can connect to this A/C using old "Online controller" app, and i can see:

@misterobotique The same question goes to you.

Sonic-Amiga commented 3 months ago

@misterobotique

Command F2 returns: G 2 4 : 0x00 0x80. Command F4 returns: G 4 0 0x00 0xA0 the first time

Sorry, couldn't understand how to interpret this. The payload (i. e. what follows response code: 'G2', 'G4') should be 4 bytes. Can you show raw dump ?

Sonic-Amiga commented 3 months ago

@hedgepigdaniel Lotta fun... If i respond '0247343000A0307B03' to F4, the controller says error 252, which is i am assuming "incompatible model". And that's the very same string my FTX20 reports, by the way.

misterobotique commented 3 months ago

@Sonic-Amiga Mixing ASCII and hexadecimal was confusing, sorry. F8 returns: 02 47 38 30 32 00 00 E1 03. F2 returns: 02 47 32 34 3A 80 80 E7 03. F4 returns: 02 47 34 30 00 A0 00 4B 03 the first time then always 02 47 34 30 00 80 00 2B 03

hedgepigdaniel commented 3 months ago

@hedgepigdaniel So, i am currently simulating CTXM60RVMA (not exactly, however) . I can connect to this A/C using old "Online controller" app, and i can see:

The unit has vertical, horizontal and 3D swing modes Correct The unit does not have "night" fan speed. Is this correct or not ? Incorrect. The IR remote has a "indoor unit quiet mode" with the night icon, and it clearly does reduce the fan speed compared to the auto mode. However, after setting night mode using the IR remote, Faikin sees it in auto mode. Setting it in night mode via Faikin seems to work correctly in that the fan is still low, and refreshing the UI shows it still in night mode.

If correct, does your second unit, CTXM25RVMA, have different set of these features ? Which one ?

As far as I can tell, the entire CTXM25RVMA, CTXM35RVMA, CTXM60RVMA have exactly the same set of features except that in the CTXM60RVMA there is only a single "intelligent eye" human presense sensor mode - on and off, whereas in the smaller units there are 3 settings - off, blow towards people, blow away from people. Same IR remote for all of them.

hedgepigdaniel commented 3 months ago

And for all of CTXM25RVMA, CTXM35RVMA, CTXM60RVMA, F8 response is 024738303230304103 or "0200"

misterobotique commented 3 months ago

@hedgepigdaniel So, i am currently simulating CTXM60RVMA (not exactly, however) . I can connect to this A/C using old "Online controller" app, and i can see:

  • The unit has vertical, horizontal and 3D swing modes
  • The unit does not have "night" fan speed. Is this correct or not ? If correct, does your second unit, CTXM25RVMA, have different set of these features ? Which one ?

@misterobotique The same question goes to you.

SxxWTES-W, SxxWTEP-W and SxxWTEV-W series only have the following features:

misterobotique commented 3 months ago
I found out what FR reports: the louver vertical position setting. Bytes Values Description
Byte(0) G -
Byte(1) R -
Byte(2) ?, 0, 1, 2, 3, 4, 5 Swing, "Comfort" mode position, upper position, high middle position, middle position, low middle position, lower position
Byte(3) 0
Byte(4) 0
Byte(5) 0

FS could be the louver horizontal position setting.

Also, I noticed F6 would returns 0 2 0 0 when "Night" mode is enabled (0 0 0 0 when disabled).

Sonic-Amiga commented 3 months ago

Also, I noticed F6 would returns 0 2 0 0 when "Night" mode is enabled (0 0 0 0 when disabled).

Thank you for this finding; i'll recheck on my unit. This is going to solve our night mode detection problem, if confirmed.

Now back to F8. I's confirmed to be a protocol version. Now my sim returns '0100', which stands for v1. aircon/get_model_info (http api) says this:

ret=OK,model=NOTSUPPORT,type=N,pv=1,cpv=0,mid=NA,s_fdir=3,en_scdltmr=1,en_onofftmr=1

Note "pv" number, which definitely stands for "protocol version". We can also recognize some optional features:

The same number is displayed as "Indoor unit software" in Onecta: select unit -> Settings -> Daikin air-to-air heat pump -> Indoor unit

I tried playing with unknown values in the simulator, but i couldn't change s_fdir. Note also model=NOTSUPPORT. I think that BRP only has some rudimentary support for protocol v1 with some fallback options. In order to get the real picture, we need to implement protocol v2 and that's going to take me some time.

revk commented 3 months ago

If we have found a night mode flag, that would be awesome.

Sonic-Amiga commented 3 months ago

Also, I noticed F6 would returns 0 2 0 0 when "Night" mode is enabled (0 0 0 0 when disabled).

Sorry, not confirmed on FTXF20:

{"protocol":"S21","dump":"024736303030344103","G6":"0004"}

both auto and night speed

MUN0X commented 3 months ago

Also, I noticed F6 would returns 0 2 0 0 when "Night" mode is enabled (0 0 0 0 when disabled).

Sorry, not confirmed on FTXF20:

{"protocol":"S21","dump":"024736303030344103","G6":"0004"}

both auto and night speed

Exactly the same with my FVXM. Returns 0004 on G6 regardless of silent setting

Sonic-Amiga commented 3 months ago

In the meanwhile i have cracked protocol v2; BRP is online and running. See my repo for updates; PRs for upstream will come later. Understood command: FC. Response is 4-character model code; reported as "model=" under aircon/get_model_info Interestingly, Onecta app still reports "not available" for Model, Serial and EEPROM . Hiding something? ;)

Sonic-Amiga commented 3 months ago

@hedgepigdaniel

the entire CTXM25RVMA, CTXM35RVMA, CTXM60RVMA have exactly the same set of features except that in the CTXM60RVMA there is only a single "intelligent eye" human presense sensor mode - on and off, whereas in the smaller units there are 3 settings - off, blow towards people, blow away from people

Great! Now i need more data from CTXM60RVMA and some other: FB FG FK FM FN FP FQ FR FS FT FV FY . The conditioner may not understand FY and reply with NAK, it's not an error.

BTW, do you know what the intelligent eye control looks like in Onecta ?

I suspect that these commands query optional features; and we're about to figure out which flag stands for "intelligent eye"

@misterobotique

mSxxWTES-W, SxxWTEP-W and SxxWTEV-W series only have the following features: vertical swing mode "night" fan speed "comfort" fan speed

Awesome, the same goes to you. Let's find out "comfort" feature indicator

hedgepigdaniel commented 3 months ago

@Sonic-Amiga I've been similarly trying all the letters of the alphabet. I'll give you my program's output - some of the data parsing functions might be wrong, but at least the hex codes are there. Below is for CTXM35RVMA:

0x46:30 (F0) { protocol: 'loopback', loopback: true } 0x46:31 (F1) { type: '0x47:31', typeAscii: 'G1', data: { length: 4, bytes: '0x31:34:48:41', ascii: '14HA', asciiHex: 1, decimals: [ 49, 52, 72, 65 ], '@numbers': [ -15, -12, 8, 1 ], coarseTemps: [ -39.5, -38, -28, -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:32 (F2) { type: '0x47:32', typeAscii: 'G2', data: { length: 4, bytes: '0x3d:3b:00:80', ascii: '=;\x00�', asciiHex: NaN, decimals: [ 61, 59, 0, 128 ], '@numbers': [ -3, -5, -64, 64 ], coarseTemps: [ -33.5, -34.5, -64, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:33 (F3) { type: '0x47:33', typeAscii: 'G3', data: { length: 4, bytes: '0x30:fe:fe:00', ascii: '0��\x00', asciiHex: NaN, decimals: [ 48, 254, 254, 0 ], '@numbers': [ -16, 190, 190, -64 ], coarseTemps: [ -40, 63, 63, -64 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:34 (F4) { type: '0x47:34', typeAscii: 'G4', data: { length: 4, bytes: '0x30:00:80:30', ascii: '0\x00�0', asciiHex: 0, decimals: [ 48, 0, 128, 48 ], '@numbers': [ -16, -64, 64, -16 ], coarseTemps: [ -40, -64, 0, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:35 (F5) { type: '0x47:35', typeAscii: 'G5', data: { length: 4, bytes: '0x37:3f:30:80', ascii: '7?0�', asciiHex: NaN, decimals: [ 55, 63, 48, 128 ], '@numbers': [ -9, -1, -16, 64 ], coarseTemps: [ -36.5, -32.5, -40, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:36 (F6) { type: '0x47:36', typeAscii: 'G6', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:37 (F7) { type: '0x47:37', typeAscii: 'G7', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:38 (F8) { type: '0x47:38', typeAscii: 'G8', data: { length: 4, bytes: '0x30:32:30:30', ascii: '0200', asciiHex: 3.2, decimals: [ 48, 50, 48, 48 ], '@numbers': [ -16, -14, -16, -16 ], coarseTemps: [ -40, -39, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 20 } } 0x46:39 (F9) { type: '0x47:39', typeAscii: 'G9', data: { length: 4, bytes: '0xb2:9a:67:30', ascii: '��g0', asciiHex: 0, decimals: [ 178, 154, 103, 48 ], '@numbers': [ 114, 90, 39, -16 ], coarseTemps: [ 25, 13, -12.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:3a (F:) NACK 0x46:3b (F;) NACK 0x46:3c (F<) NACK 0x46:3d (F=) NACK 0x46:3e (F>) NACK 0x46:3f (F?) NACK 0x46:40 (F@) NACK 0x46:41 (FA) { type: '0x47:41', typeAscii: 'GA', data: { length: 4, bytes: '0x38:31:32:30', ascii: '8120', asciiHex: 53.6, decimals: [ 56, 49, 50, 48 ], '@numbers': [ -8, -15, -14, -16 ], coarseTemps: [ -36, -39.5, -39, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 218 } } 0x46:42 (FB) { type: '0x47:42', typeAscii: 'GB', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:43 (FC) { type: '0x47:43', typeAscii: 'GC', data: { length: 4, bytes: '0x31:42:45:30', ascii: '1BE0', asciiHex: 376.1, decimals: [ 49, 66, 69, 48 ], '@numbers': [ -15, 2, 5, -16 ], coarseTemps: [ -39.5, -31, -29.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:44 (FD) NACK 0x46:45 (FE) NACK 0x46:46 (FF) NACK 0x46:47 (FG) { type: '0x47:47', typeAscii: 'GG', data: { length: 4, bytes: '0x30:44:30:30', ascii: '0D00', asciiHex: 20.8, decimals: [ 48, 68, 48, 48 ], '@numbers': [ -16, 4, -16, -16 ], coarseTemps: [ -40, -30, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:48 (FH) TIMEOUT 0x46:49 (FI) NACK 0x46:4a (FJ) TIMEOUT 0x46:4b (FK) { type: '0x47:4b', typeAscii: 'GK', data: { length: 4, bytes: '0x30:3a:35:31', ascii: '0:51', asciiHex: 2.1, decimals: [ 48, 58, 53, 49 ], '@numbers': [ -16, -6, -11, -15 ], coarseTemps: [ -40, -35, -37.5, -39.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:4c (FL) { type: '0x47:4c', typeAscii: 'GL', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4d (FM) { type: '0x47:4d', typeAscii: 'GM', data: { length: 4, bytes: '0x31:36:33:30', ascii: '1630', asciiHex: 86.5, decimals: [ 49, 54, 51, 48 ], '@numbers': [ -15, -10, -13, -16 ], coarseTemps: [ -39.5, -37, -38.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 361 } } 0x46:4e (FN) { type: '0x47:4e', typeAscii: 'GN', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4f (FO) NACK 0x46:50 (FP) { type: '0x47:50', typeAscii: 'GP', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:51 (FQ) { type: '0x47:51', typeAscii: 'GQ', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:52 (FR) { type: '0x47:52', typeAscii: 'GR', data: { length: 4, bytes: '0x3f:4f:30:30', ascii: '?O00', asciiHex: 0, decimals: [ 63, 79, 48, 48 ], '@numbers': [ -1, 15, -16, -16 ], coarseTemps: [ -32.5, -24.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:53 (FS) { type: '0x47:53', typeAscii: 'GS', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:54 (FT) { type: '0x47:54', typeAscii: 'GT', data: { length: 4, bytes: '0x33:30:30:30', ascii: '3000', asciiHex: 0.3, decimals: [ 51, 48, 48, 48 ], '@numbers': [ -13, -16, -16, -16 ], coarseTemps: [ -38.5, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 3 } } 0x46:55 (FU) NACK 0x46:56 (FV) NACK 0x46:57 (FW) NACK 0x46:58 (FX) NACK 0x46:59 (FY) NACK 0x46:5a (FZ) NACK 0x46:5b (F[) NACK 0x46:5c (F\) NACK 0x46:5d (F]) NACK 0x46:5e (F^) NACK 0x46:5f (F_) NACK 0x46:60 (F`) NACK 0x46:61 (Fa) NACK 0x46:62 (Fb) NACK 0x46:63 (Fc) NACK 0x46:64 (Fd) NACK 0x46:65 (Fe) TIMEOUT 0x46:66 (Ff) NACK 0x46:67 (Fg) NACK 0x46:68 (Fh) NACK 0x46:69 (Fi) NACK 0x46:6a (Fj) NACK 0x46:6b (Fk) NACK 0x46:6c (Fl) NACK 0x46:6d (Fm) NACK 0x46:6e (Fn) NACK 0x46:6f (Fo) NACK 0x46:70 (Fp) NACK 0x46:71 (Fq) NACK 0x46:72 (Fr) NACK 0x46:73 (Fs) NACK 0x46:74 (Ft) NACK 0x46:75 (Fu) NACK 0x46:76 (Fv) NACK 0x46:77 (Fw) NACK 0x46:78 (Fx) NACK 0x46:79 (Fy) NACK 0x46:7a (Fz) NACK 0x52:30 (R0) NACK 0x52:31 (R1) NACK 0x52:32 (R2) NACK 0x52:33 (R3) NACK 0x52:34 (R4) NACK 0x52:35 (R5) NACK 0x52:36 (R6) NACK 0x52:37 (R7) NACK 0x52:38 (R8) NACK 0x52:39 (R9) NACK 0x52:3a (R:) NACK 0x52:3b (R;) NACK 0x52:3c (R<) NACK 0x52:3d (R=) NACK 0x52:3e (R>) NACK 0x52:3f (R?) NACK 0x52:40 (R@) NACK 0x52:41 (RA) { type: '0x53:41', typeAscii: 'SA', data: { length: 1, bytes: '0x31', ascii: '1', asciiHex: 0.1, decimals: [ 49 ], '@numbers': [ -15 ], coarseTemps: [ -39.5 ], signedNumericDecimal: null, unsignedNumericDecimal: 1 } } 0x52:42 (RB) 0x52:43 (RC) { type: '0x53:43', typeAscii: 'SC', data: { length: 4, bytes: '0x32:32:30:2b', ascii: '220+', asciiHex: 3.4, decimals: [ 50, 50, 48, 43 ], '@numbers': [ -14, -14, -16, -21 ], coarseTemps: [ -39, -39, -40, -42.5 ], signedNumericDecimal: 22, unsignedNumericDecimal: 22 } } 0x52:44 (RD) { type: '0x53:44', typeAscii: 'SD', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:45 (RE) { type: '0x53:45', typeAscii: 'SE', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:46 (RF) 0x52:47 (RG) { type: '0x53:47', typeAscii: 'SG', data: { length: 1, bytes: '0x41', ascii: 'A', asciiHex: 1, decimals: [ 65 ], '@numbers': [ 1 ], coarseTemps: [ -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x52:48 (RH) 0x52:49 (RI) { type: '0x53:49', typeAscii: 'SI', data: { length: 4, bytes: '0x30:31:34:2b', ascii: '014+', asciiHex: 104, decimals: [ 48, 49, 52, 43 ], '@numbers': [ -16, -15, -12, -21 ], coarseTemps: [ -40, -39.5, -38, -42.5 ], signedNumericDecimal: 410, unsignedNumericDecimal: 410 } } 0x52:4a (RJ) NACK 0x52:4b (RK) { type: '0x53:4b', typeAscii: 'SK', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:4c (RL) 0x52:4d (RM) { type: '0x53:4d', typeAscii: 'SM', data: { length: 4, bytes: '0x32:37:30:2b', ascii: '270+', asciiHex: 11.4, decimals: [ 50, 55, 48, 43 ], '@numbers': [ -14, -9, -16, -21 ], coarseTemps: [ -39, -36.5, -40, -42.5 ], signedNumericDecimal: 72, unsignedNumericDecimal: 72 } } 0x52:4e (RN) { type: '0x53:4e', typeAscii: 'SN', data: { length: 4, bytes: '0x32:37:30:2b', ascii: '270+', asciiHex: 11.4, decimals: [ 50, 55, 48, 43 ], '@numbers': [ -14, -9, -16, -21 ], coarseTemps: [ -39, -36.5, -40, -42.5 ], signedNumericDecimal: 72, unsignedNumericDecimal: 72 } } 0x52:4f (RO) NACK 0x52:50 (RP) NACK 0x52:51 (RQ) NACK 0x52:52 (RR) NACK 0x52:53 (RS) NACK 0x52:54 (RT) NACK 0x52:55 (RU) NACK 0x52:56 (RV) NACK 0x52:57 (RW) { type: '0x53:57', typeAscii: 'SW', data: { length: 2, bytes: '0x30:30', ascii: '00', asciiHex: 0, decimals: [ 48, 48 ], '@numbers': [ -16, -16 ], coarseTemps: [ -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:58 (RX) 0x52:59 (RY) NACK 0x52:5a (RZ) NACK 0x52:5b (R[) NACK 0x52:5c (R\) NACK 0x52:5d (R]) NACK 0x52:5e (R^) NACK 0x52:5f (R_) NACK 0x52:60 (R`) NACK 0x52:61 (Ra) { type: '0x53:61', typeAscii: 'Sa', data: { length: 4, bytes: '0x35:32:31:2b', ascii: '521+', asciiHex: 29.3, decimals: [ 53, 50, 49, 43 ], '@numbers': [ -11, -14, -15, -21 ], coarseTemps: [ -37.5, -39, -39.5, -42.5 ], signedNumericDecimal: 125, unsignedNumericDecimal: 125 } } 0x52:62 (Rb) { type: '0x53:62', typeAscii: 'Sb', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:63 (Rc) 0x52:64 (Rd) { type: '0x53:64', typeAscii: 'Sd', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:65 (Re) { type: '0x53:65', typeAscii: 'Se', data: { length: 3, bytes: '0x35:35:30', ascii: '550', asciiHex: 8.5, decimals: [ 53, 53, 48 ], '@numbers': [ -11, -11, -16 ], coarseTemps: [ -37.5, -37.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 55 } } 0x52:66 (Rf) 0x52:67 (Rg) { type: '0x53:67', typeAscii: 'Sg', data: { length: 1, bytes: '0x30', ascii: '0', asciiHex: 0, decimals: [ 48 ], '@numbers': [ -16 ], coarseTemps: [ -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:68 (Rh) 0x52:69 (Ri) NACK 0x52:6a (Rj) NACK 0x52:6b (Rk) NACK 0x52:6c (Rl) NACK 0x52:6d (Rm) NACK 0x52:6e (Rn) NACK 0x52:6f (Ro) NACK 0x52:70 (Rp) NACK 0x52:71 (Rq) NACK 0x52:72 (Rr) NACK 0x52:73 (Rs) NACK 0x52:74 (Rt) NACK 0x52:75 (Ru) NACK 0x52:76 (Rv) NACK 0x52:77 (Rw) NACK 0x52:78 (Rx) NACK 0x52:79 (Ry) NACK 0x52:7a (Rz) Error: Invalid checksum 204 (expected 205) at parsePacket (/home/daniel/code/oss/Faikin-force/src/index.ts:50:7) at logCommand (/home/daniel/code/oss/Faikin-force/src/index.ts:198:34) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: '0x53:7a', typeAscii: 'Sz', data: null }
hedgepigdaniel commented 3 months ago

CTXM25RVMA

0x46:30 (F0) NACK 0x46:31 (F1) { type: '0x47:31', typeAscii: 'G1', data: { length: 4, bytes: '0x31:34:48:41', ascii: '14HA', asciiHex: 1, decimals: [ 49, 52, 72, 65 ], '@numbers': [ -15, -12, 8, 1 ], coarseTemps: [ -39.5, -38, -28, -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:32 (F2) { type: '0x47:32', typeAscii: 'G2', data: { length: 4, bytes: '0x3d:3b:00:80', ascii: '=;\x00�', asciiHex: NaN, decimals: [ 61, 59, 0, 128 ], '@numbers': [ -3, -5, -64, 64 ], coarseTemps: [ -33.5, -34.5, -64, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:33 (F3) { type: '0x47:33', typeAscii: 'G3', data: { length: 4, bytes: '0x30:fe:fe:00', ascii: '0��\x00', asciiHex: NaN, decimals: [ 48, 254, 254, 0 ], '@numbers': [ -16, 190, 190, -64 ], coarseTemps: [ -40, 63, 63, -64 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:34 (F4) { type: '0x47:34', typeAscii: 'G4', data: { length: 4, bytes: '0x30:00:80:30', ascii: '0\x00�0', asciiHex: 0, decimals: [ 48, 0, 128, 48 ], '@numbers': [ -16, -64, 64, -16 ], coarseTemps: [ -40, -64, 0, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:35 (F5) { type: '0x47:35', typeAscii: 'G5', data: { length: 4, bytes: '0x37:3f:30:80', ascii: '7?0�', asciiHex: NaN, decimals: [ 55, 63, 48, 128 ], '@numbers': [ -9, -1, -16, 64 ], coarseTemps: [ -36.5, -32.5, -40, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:36 (F6) { type: '0x47:36', typeAscii: 'G6', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:37 (F7) { type: '0x47:37', typeAscii: 'G7', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:38 (F8) { type: '0x47:38', typeAscii: 'G8', data: { length: 4, bytes: '0x30:32:30:30', ascii: '0200', asciiHex: 3.2, decimals: [ 48, 50, 48, 48 ], '@numbers': [ -16, -14, -16, -16 ], coarseTemps: [ -40, -39, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 20 } } 0x46:39 (F9) { type: '0x47:39', typeAscii: 'G9', data: { length: 4, bytes: '0xae:a0:6c:30', ascii: '��l0', asciiHex: 0, decimals: [ 174, 160, 108, 48 ], '@numbers': [ 110, 96, 44, -16 ], coarseTemps: [ 23, 16, -10, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:3a (F:) NACK 0x46:3b (F;) NACK 0x46:3c (F<) NACK 0x46:3d (F=) NACK 0x46:3e (F>) NACK 0x46:3f (F?) NACK 0x46:40 (F@) NACK 0x46:41 (FA) { type: '0x47:41', typeAscii: 'GA', data: { length: 4, bytes: '0x44:31:32:30', ascii: 'D120', asciiHex: 54.1, decimals: [ 68, 49, 50, 48 ], '@numbers': [ 4, -15, -14, -16 ], coarseTemps: [ -30, -39.5, -39, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:42 (FB) { type: '0x47:42', typeAscii: 'GB', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:43 (FC) { type: '0x47:43', typeAscii: 'GC', data: { length: 4, bytes: '0x30:42:45:30', ascii: '0BE0', asciiHex: 376, decimals: [ 48, 66, 69, 48 ], '@numbers': [ -16, 2, 5, -16 ], coarseTemps: [ -40, -31, -29.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:44 (FD) NACK 0x46:45 (FE) NACK 0x46:46 (FF) NACK 0x46:47 (FG) { type: '0x47:47', typeAscii: 'GG', data: { length: 4, bytes: '0x30:43:30:30', ascii: '0C00', asciiHex: 19.2, decimals: [ 48, 67, 48, 48 ], '@numbers': [ -16, 3, -16, -16 ], coarseTemps: [ -40, -30.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:48 (FH) TIMEOUT 0x46:49 (FI) NACK 0x46:4a (FJ) TIMEOUT 0x46:4b (FK) { type: '0x47:4b', typeAscii: 'GK', data: { length: 4, bytes: '0x30:3a:35:31', ascii: '0:51', asciiHex: 2.1, decimals: [ 48, 58, 53, 49 ], '@numbers': [ -16, -6, -11, -15 ], coarseTemps: [ -40, -35, -37.5, -39.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:4c (FL) { type: '0x47:4c', typeAscii: 'GL', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4d (FM) { type: '0x47:4d', typeAscii: 'GM', data: { length: 4, bytes: '0x46:38:33:30', ascii: 'F830', asciiHex: 91.1, decimals: [ 70, 56, 51, 48 ], '@numbers': [ 6, -8, -13, -16 ], coarseTemps: [ -29, -36, -38.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:4e (FN) { type: '0x47:4e', typeAscii: 'GN', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4f (FO) NACK 0x46:50 (FP) { type: '0x47:50', typeAscii: 'GP', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:51 (FQ) { type: '0x47:51', typeAscii: 'GQ', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:52 (FR) { type: '0x47:52', typeAscii: 'GR', data: { length: 4, bytes: '0x3f:4f:30:30', ascii: '?O00', asciiHex: 0, decimals: [ 63, 79, 48, 48 ], '@numbers': [ -1, 15, -16, -16 ], coarseTemps: [ -32.5, -24.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:53 (FS) { type: '0x47:53', typeAscii: 'GS', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:54 (FT) { type: '0x47:54', typeAscii: 'GT', data: { length: 4, bytes: '0x33:30:30:30', ascii: '3000', asciiHex: 0.3, decimals: [ 51, 48, 48, 48 ], '@numbers': [ -13, -16, -16, -16 ], coarseTemps: [ -38.5, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 3 } } 0x46:55 (FU) NACK 0x46:56 (FV) NACK 0x46:57 (FW) NACK 0x46:58 (FX) NACK 0x46:59 (FY) NACK 0x46:5a (FZ) NACK 0x46:5b (F[) NACK 0x46:5c (F\) NACK 0x46:5d (F]) NACK 0x46:5e (F^) NACK 0x46:5f (F_) NACK 0x46:60 (F`) NACK 0x46:61 (Fa) NACK 0x46:62 (Fb) NACK 0x46:63 (Fc) NACK 0x46:64 (Fd) NACK 0x46:65 (Fe) TIMEOUT 0x46:66 (Ff) NACK 0x46:67 (Fg) NACK 0x46:68 (Fh) NACK 0x46:69 (Fi) NACK 0x46:6a (Fj) NACK 0x46:6b (Fk) NACK 0x46:6c (Fl) NACK 0x46:6d (Fm) NACK 0x46:6e (Fn) NACK 0x46:6f (Fo) NACK 0x46:70 (Fp) NACK 0x46:71 (Fq) NACK 0x46:72 (Fr) NACK 0x46:73 (Fs) NACK 0x46:74 (Ft) NACK 0x46:75 (Fu) NACK 0x46:76 (Fv) NACK 0x46:77 (Fw) NACK 0x46:78 (Fx) NACK 0x46:79 (Fy) NACK 0x46:7a (Fz) NACK 0x52:30 (R0) NACK 0x52:31 (R1) NACK 0x52:32 (R2) NACK 0x52:33 (R3) NACK 0x52:34 (R4) NACK 0x52:35 (R5) NACK 0x52:36 (R6) NACK 0x52:37 (R7) NACK 0x52:38 (R8) NACK 0x52:39 (R9) NACK 0x52:3a (R:) NACK 0x52:3b (R;) NACK 0x52:3c (R<) NACK 0x52:3d (R=) NACK 0x52:3e (R>) NACK 0x52:3f (R?) NACK 0x52:40 (R@) NACK 0x52:41 (RA) { type: '0x53:41', typeAscii: 'SA', data: { length: 1, bytes: '0x31', ascii: '1', asciiHex: 0.1, decimals: [ 49 ], '@numbers': [ -15 ], coarseTemps: [ -39.5 ], signedNumericDecimal: null, unsignedNumericDecimal: 1 } } 0x52:42 (RB) 0x52:43 (RC) { type: '0x53:43', typeAscii: 'SC', data: { length: 4, bytes: '0x32:32:30:2b', ascii: '220+', asciiHex: 3.4, decimals: [ 50, 50, 48, 43 ], '@numbers': [ -14, -14, -16, -21 ], coarseTemps: [ -39, -39, -40, -42.5 ], signedNumericDecimal: 22, unsignedNumericDecimal: 22 } } 0x52:44 (RD) { type: '0x53:44', typeAscii: 'SD', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:45 (RE) { type: '0x53:45', typeAscii: 'SE', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:46 (RF) 0x52:47 (RG) { type: '0x53:47', typeAscii: 'SG', data: { length: 1, bytes: '0x41', ascii: 'A', asciiHex: 1, decimals: [ 65 ], '@numbers': [ 1 ], coarseTemps: [ -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x52:48 (RH) 0x52:49 (RI) { type: '0x53:49', typeAscii: 'SI', data: { length: 4, bytes: '0x30:30:33:2b', ascii: '003+', asciiHex: 76.8, decimals: [ 48, 48, 51, 43 ], '@numbers': [ -16, -16, -13, -21 ], coarseTemps: [ -40, -40, -38.5, -42.5 ], signedNumericDecimal: 300, unsignedNumericDecimal: 300 } } 0x52:4a (RJ) NACK 0x52:4b (RK) { type: '0x53:4b', typeAscii: 'SK', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:4c (RL) 0x52:4d (RM) { type: '0x53:4d', typeAscii: 'SM', data: { length: 4, bytes: '0x32:37:30:2b', ascii: '270+', asciiHex: 11.4, decimals: [ 50, 55, 48, 43 ], '@numbers': [ -14, -9, -16, -21 ], coarseTemps: [ -39, -36.5, -40, -42.5 ], signedNumericDecimal: 72, unsignedNumericDecimal: 72 } } 0x52:4e (RN) { type: '0x53:4e', typeAscii: 'SN', data: { length: 4, bytes: '0x32:37:30:2b', ascii: '270+', asciiHex: 11.4, decimals: [ 50, 55, 48, 43 ], '@numbers': [ -14, -9, -16, -21 ], coarseTemps: [ -39, -36.5, -40, -42.5 ], signedNumericDecimal: 72, unsignedNumericDecimal: 72 } } 0x52:4f (RO) NACK 0x52:50 (RP) NACK 0x52:51 (RQ) NACK 0x52:52 (RR) NACK 0x52:53 (RS) NACK 0x52:54 (RT) NACK 0x52:55 (RU) NACK 0x52:56 (RV) NACK 0x52:57 (RW) { type: '0x53:57', typeAscii: 'SW', data: { length: 2, bytes: '0x30:30', ascii: '00', asciiHex: 0, decimals: [ 48, 48 ], '@numbers': [ -16, -16 ], coarseTemps: [ -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:58 (RX) 0x52:59 (RY) NACK 0x52:5a (RZ) NACK 0x52:5b (R[) NACK 0x52:5c (R\) NACK 0x52:5d (R]) NACK 0x52:5e (R^) NACK 0x52:5f (R_) NACK 0x52:60 (R`) NACK 0x52:61 (Ra) { type: '0x53:61', typeAscii: 'Sa', data: { length: 4, bytes: '0x30:36:31:2b', ascii: '061+', asciiHex: 35.2, decimals: [ 48, 54, 49, 43 ], '@numbers': [ -16, -10, -15, -21 ], coarseTemps: [ -40, -37, -39.5, -42.5 ], signedNumericDecimal: 160, unsignedNumericDecimal: 160 } } 0x52:62 (Rb) { type: '0x53:62', typeAscii: 'Sb', data: { length: 3, bytes: '0x31:30:30', ascii: '100', asciiHex: 0.1, decimals: [ 49, 48, 48 ], '@numbers': [ -15, -16, -16 ], coarseTemps: [ -39.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 1 } } 0x52:63 (Rc) 0x52:64 (Rd) { type: '0x53:64', typeAscii: 'Sd', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:65 (Re) { type: '0x53:65', typeAscii: 'Se', data: { length: 3, bytes: '0x31:36:30', ascii: '160', asciiHex: 9.7, decimals: [ 49, 54, 48 ], '@numbers': [ -15, -10, -16 ], coarseTemps: [ -39.5, -37, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 61 } } 0x52:66 (Rf) 0x52:67 (Rg) { type: '0x53:67', typeAscii: 'Sg', data: { length: 1, bytes: '0x30', ascii: '0', asciiHex: 0, decimals: [ 48 ], '@numbers': [ -16 ], coarseTemps: [ -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:68 (Rh) 0x52:69 (Ri) NACK 0x52:6a (Rj) NACK 0x52:6b (Rk) NACK 0x52:6c (Rl) NACK 0x52:6d (Rm) NACK 0x52:6e (Rn) NACK 0x52:6f (Ro) NACK 0x52:70 (Rp) NACK 0x52:71 (Rq) NACK 0x52:72 (Rr) NACK 0x52:73 (Rs) NACK 0x52:74 (Rt) NACK 0x52:75 (Ru) NACK 0x52:76 (Rv) NACK 0x52:77 (Rw) NACK 0x52:78 (Rx) NACK 0x52:79 (Ry) NACK 0x52:7a (Rz) Error: Invalid checksum 204 (expected 205) at parsePacket (/home/daniel/code/oss/Faikin-force/src/index.ts:50:7) at logCommand (/home/daniel/code/oss/Faikin-force/src/index.ts:198:34) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: '0x53:7a', typeAscii: 'Sz', data: null }

CTXM60RVMA

0x46:30 (F0) NACK 0x46:31 (F1) { type: '0x47:31', typeAscii: 'G1', data: { length: 4, bytes: '0x30:34:46:41', ascii: '04FA', asciiHex: 4486.4, decimals: [ 48, 52, 70, 65 ], '@numbers': [ -16, -12, 6, 1 ], coarseTemps: [ -40, -38, -29, -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:32 (F2) { type: '0x47:32', typeAscii: 'G2', data: { length: 4, bytes: '0x3d:3b:00:80', ascii: '=;\x00�', asciiHex: NaN, decimals: [ 61, 59, 0, 128 ], '@numbers': [ -3, -5, -64, 64 ], coarseTemps: [ -33.5, -34.5, -64, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:33 (F3) { type: '0x47:33', typeAscii: 'G3', data: { length: 4, bytes: '0x30:fe:fe:00', ascii: '0��\x00', asciiHex: NaN, decimals: [ 48, 254, 254, 0 ], '@numbers': [ -16, 190, 190, -64 ], coarseTemps: [ -40, 63, 63, -64 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:34 (F4) { type: '0x47:34', typeAscii: 'G4', data: { length: 4, bytes: '0x30:00:80:30', ascii: '0\x00�0', asciiHex: 0, decimals: [ 48, 0, 128, 48 ], '@numbers': [ -16, -64, 64, -16 ], coarseTemps: [ -40, -64, 0, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:35 (F5) { type: '0x47:35', typeAscii: 'G5', data: { length: 4, bytes: '0x37:3f:30:80', ascii: '7?0�', asciiHex: NaN, decimals: [ 55, 63, 48, 128 ], '@numbers': [ -9, -1, -16, 64 ], coarseTemps: [ -36.5, -32.5, -40, 0 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:36 (F6) { type: '0x47:36', typeAscii: 'G6', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:37 (F7) { type: '0x47:37', typeAscii: 'G7', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:38 (F8) { type: '0x47:38', typeAscii: 'G8', data: { length: 4, bytes: '0x30:32:30:30', ascii: '0200', asciiHex: 3.2, decimals: [ 48, 50, 48, 48 ], '@numbers': [ -16, -14, -16, -16 ], coarseTemps: [ -40, -39, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 20 } } 0x46:39 (F9) { type: '0x47:39', typeAscii: 'G9', data: { length: 4, bytes: '0xa4:a0:7b:30', ascii: '��{0', asciiHex: 0, decimals: [ 164, 160, 123, 48 ], '@numbers': [ 100, 96, 59, -16 ], coarseTemps: [ 18, 16, -2.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:3a (F:) NACK 0x46:3b (F;) NACK 0x46:3c (F<) NACK 0x46:3d (F=) NACK 0x46:3e (F>) NACK 0x46:3f (F?) NACK 0x46:40 (F@) NACK 0x46:41 (FA) { type: '0x47:41', typeAscii: 'GA', data: { length: 4, bytes: '0x30:41:32:30', ascii: '0A20', asciiHex: 67.2, decimals: [ 48, 65, 50, 48 ], '@numbers': [ -16, 1, -14, -16 ], coarseTemps: [ -40, -31.5, -39, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:42 (FB) { type: '0x47:42', typeAscii: 'GB', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:43 (FC) { type: '0x47:43', typeAscii: 'GC', data: { length: 4, bytes: '0x34:42:45:30', ascii: '4BE0', asciiHex: 376.4, decimals: [ 52, 66, 69, 48 ], '@numbers': [ -12, 2, 5, -16 ], coarseTemps: [ -38, -31, -29.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:44 (FD) NACK 0x46:45 (FE) NACK 0x46:46 (FF) NACK 0x46:47 (FG) { type: '0x47:47', typeAscii: 'GG', data: { length: 4, bytes: '0x30:39:30:30', ascii: '0900', asciiHex: 14.4, decimals: [ 48, 57, 48, 48 ], '@numbers': [ -16, -7, -16, -16 ], coarseTemps: [ -40, -35.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 90 } } 0x46:48 (FH) TIMEOUT 0x46:49 (FI) NACK 0x46:4a (FJ) TIMEOUT 0x46:4b (FK) { type: '0x47:4b', typeAscii: 'GK', data: { length: 4, bytes: '0x30:3a:35:31', ascii: '0:51', asciiHex: 2.1, decimals: [ 48, 58, 53, 49 ], '@numbers': [ -16, -6, -11, -15 ], coarseTemps: [ -40, -35, -37.5, -39.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:4c (FL) { type: '0x47:4c', typeAscii: 'GL', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4d (FM) { type: '0x47:4d', typeAscii: 'GM', data: { length: 4, bytes: '0x34:42:38:30', ascii: '4B80', asciiHex: 222.8, decimals: [ 52, 66, 56, 48 ], '@numbers': [ -12, 2, -8, -16 ], coarseTemps: [ -38, -31, -36, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:4e (FN) { type: '0x47:4e', typeAscii: 'GN', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:4f (FO) NACK 0x46:50 (FP) { type: '0x47:50', typeAscii: 'GP', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:51 (FQ) { type: '0x47:51', typeAscii: 'GQ', data: { length: 4, bytes: '0x46:46:46:46', ascii: 'FFFF', asciiHex: 6553.5, decimals: [ 70, 70, 70, 70 ], '@numbers': [ 6, 6, 6, 6 ], coarseTemps: [ -29, -29, -29, -29 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:52 (FR) { type: '0x47:52', typeAscii: 'GR', data: { length: 4, bytes: '0x3f:4f:30:30', ascii: '?O00', asciiHex: 0, decimals: [ 63, 79, 48, 48 ], '@numbers': [ -1, 15, -16, -16 ], coarseTemps: [ -32.5, -24.5, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x46:53 (FS) { type: '0x47:53', typeAscii: 'GS', data: { length: 4, bytes: '0x30:30:30:30', ascii: '0000', asciiHex: 0, decimals: [ 48, 48, 48, 48 ], '@numbers': [ -16, -16, -16, -16 ], coarseTemps: [ -40, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x46:54 (FT) { type: '0x47:54', typeAscii: 'GT', data: { length: 4, bytes: '0x34:30:30:30', ascii: '4000', asciiHex: 0.4, decimals: [ 52, 48, 48, 48 ], '@numbers': [ -12, -16, -16, -16 ], coarseTemps: [ -38, -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 4 } } 0x46:55 (FU) NACK 0x46:56 (FV) NACK 0x46:57 (FW) NACK 0x46:58 (FX) NACK 0x46:59 (FY) NACK 0x46:5a (FZ) NACK 0x46:5b (F[) NACK 0x46:5c (F\) NACK 0x46:5d (F]) NACK 0x46:5e (F^) NACK 0x46:5f (F_) NACK 0x46:60 (F`) NACK 0x46:61 (Fa) NACK 0x46:62 (Fb) NACK 0x46:63 (Fc) NACK 0x46:64 (Fd) NACK 0x46:65 (Fe) TIMEOUT 0x46:66 (Ff) NACK 0x46:67 (Fg) NACK 0x46:68 (Fh) NACK 0x46:69 (Fi) NACK 0x46:6a (Fj) NACK 0x46:6b (Fk) NACK 0x46:6c (Fl) NACK 0x46:6d (Fm) NACK 0x46:6e (Fn) NACK 0x46:6f (Fo) NACK 0x46:70 (Fp) NACK 0x46:71 (Fq) NACK 0x46:72 (Fr) NACK 0x46:73 (Fs) NACK 0x46:74 (Ft) NACK 0x46:75 (Fu) NACK 0x46:76 (Fv) NACK 0x46:77 (Fw) NACK 0x46:78 (Fx) NACK 0x46:79 (Fy) NACK 0x46:7a (Fz) NACK 0x52:30 (R0) NACK 0x52:31 (R1) NACK 0x52:32 (R2) NACK 0x52:33 (R3) NACK 0x52:34 (R4) NACK 0x52:35 (R5) NACK 0x52:36 (R6) NACK 0x52:37 (R7) NACK 0x52:38 (R8) NACK 0x52:39 (R9) NACK 0x52:3a (R:) NACK 0x52:3b (R;) NACK 0x52:3c (R<) NACK 0x52:3d (R=) NACK 0x52:3e (R>) NACK 0x52:3f (R?) NACK 0x52:40 (R@) NACK 0x52:41 (RA) { type: '0x53:41', typeAscii: 'SA', data: { length: 1, bytes: '0x30', ascii: '0', asciiHex: 0, decimals: [ 48 ], '@numbers': [ -16 ], coarseTemps: [ -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:42 (RB) 0x52:43 (RC) { type: '0x53:43', typeAscii: 'SC', data: { length: 4, bytes: '0x31:32:30:2b', ascii: '120+', asciiHex: 3.3, decimals: [ 49, 50, 48, 43 ], '@numbers': [ -15, -14, -16, -21 ], coarseTemps: [ -39.5, -39, -40, -42.5 ], signedNumericDecimal: 21, unsignedNumericDecimal: 21 } } 0x52:44 (RD) { type: '0x53:44', typeAscii: 'SD', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:45 (RE) { type: '0x53:45', typeAscii: 'SE', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:46 (RF) 0x52:47 (RG) { type: '0x53:47', typeAscii: 'SG', data: { length: 1, bytes: '0x41', ascii: 'A', asciiHex: 1, decimals: [ 65 ], '@numbers': [ 1 ], coarseTemps: [ -31.5 ], signedNumericDecimal: null, unsignedNumericDecimal: NaN } } 0x52:48 (RH) 0x52:49 (RI) { type: '0x53:49', typeAscii: 'SI', data: { length: 4, bytes: '0x30:31:34:2b', ascii: '014+', asciiHex: 104, decimals: [ 48, 49, 52, 43 ], '@numbers': [ -16, -15, -12, -21 ], coarseTemps: [ -40, -39.5, -38, -42.5 ], signedNumericDecimal: 410, unsignedNumericDecimal: 410 } } 0x52:4a (RJ) NACK 0x52:4b (RK) { type: '0x53:4b', typeAscii: 'SK', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:4c (RL) 0x52:4d (RM) { type: '0x53:4d', typeAscii: 'SM', data: { length: 4, bytes: '0x36:31:31:2b', ascii: '611+', asciiHex: 27.8, decimals: [ 54, 49, 49, 43 ], '@numbers': [ -10, -15, -15, -21 ], coarseTemps: [ -37, -39.5, -39.5, -42.5 ], signedNumericDecimal: 116, unsignedNumericDecimal: 116 } } 0x52:4e (RN) { type: '0x53:4e', typeAscii: 'SN', data: { length: 4, bytes: '0x36:31:31:2b', ascii: '611+', asciiHex: 27.8, decimals: [ 54, 49, 49, 43 ], '@numbers': [ -10, -15, -15, -21 ], coarseTemps: [ -37, -39.5, -39.5, -42.5 ], signedNumericDecimal: 116, unsignedNumericDecimal: 116 } } 0x52:4f (RO) NACK 0x52:50 (RP) NACK 0x52:51 (RQ) NACK 0x52:52 (RR) NACK 0x52:53 (RS) NACK 0x52:54 (RT) NACK 0x52:55 (RU) NACK 0x52:56 (RV) NACK 0x52:57 (RW) { type: '0x53:57', typeAscii: 'SW', data: { length: 2, bytes: '0x30:30', ascii: '00', asciiHex: 0, decimals: [ 48, 48 ], '@numbers': [ -16, -16 ], coarseTemps: [ -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:58 (RX) 0x52:59 (RY) NACK 0x52:5a (RZ) NACK 0x52:5b (R[) NACK 0x52:5c (R\) NACK 0x52:5d (R]) NACK 0x52:5e (R^) NACK 0x52:5f (R_) NACK 0x52:60 (R`) NACK 0x52:61 (Ra) { type: '0x53:61', typeAscii: 'Sa', data: { length: 4, bytes: '0x30:36:31:2b', ascii: '061+', asciiHex: 35.2, decimals: [ 48, 54, 49, 43 ], '@numbers': [ -16, -10, -15, -21 ], coarseTemps: [ -40, -37, -39.5, -42.5 ], signedNumericDecimal: 160, unsignedNumericDecimal: 160 } } 0x52:62 (Rb) { type: '0x53:62', typeAscii: 'Sb', data: { length: 3, bytes: '0x30:30:30', ascii: '000', asciiHex: 0, decimals: [ 48, 48, 48 ], '@numbers': [ -16, -16, -16 ], coarseTemps: [ -40, -40, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:63 (Rc) 0x52:64 (Rd) { type: '0x53:64', typeAscii: 'Sd', data: { length: 3, bytes: '0x38:34:30', ascii: '840', asciiHex: 7.2, decimals: [ 56, 52, 48 ], '@numbers': [ -8, -12, -16 ], coarseTemps: [ -36, -38, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 48 } } 0x52:65 (Re) { type: '0x53:65', typeAscii: 'Se', data: { length: 3, bytes: '0x37:37:30', ascii: '770', asciiHex: 11.9, decimals: [ 55, 55, 48 ], '@numbers': [ -9, -9, -16 ], coarseTemps: [ -36.5, -36.5, -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 77 } } 0x52:66 (Rf) 0x52:67 (Rg) { type: '0x53:67', typeAscii: 'Sg', data: { length: 1, bytes: '0x30', ascii: '0', asciiHex: 0, decimals: [ 48 ], '@numbers': [ -16 ], coarseTemps: [ -40 ], signedNumericDecimal: null, unsignedNumericDecimal: 0 } } 0x52:68 (Rh) 0x52:69 (Ri) NACK 0x52:6a (Rj) NACK 0x52:6b (Rk) NACK 0x52:6c (Rl) NACK 0x52:6d (Rm) NACK 0x52:6e (Rn) NACK 0x52:6f (Ro) NACK 0x52:70 (Rp) NACK 0x52:71 (Rq) NACK 0x52:72 (Rr) NACK 0x52:73 (Rs) NACK 0x52:74 (Rt) NACK 0x52:75 (Ru) NACK 0x52:76 (Rv) NACK 0x52:77 (Rw) NACK 0x52:78 (Rx) NACK 0x52:79 (Ry) NACK 0x52:7a (Rz) Error: Invalid checksum 204 (expected 205) at parsePacket (/home/daniel/code/oss/Faikin-force/src/index.ts:50:7) at logCommand (/home/daniel/code/oss/Faikin-force/src/index.ts:198:34) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: '0x53:7a', typeAscii: 'Sz', data: null }
misterobotique commented 3 months ago

I checked again and there is no mistake: with my air conditioner, "night" mode status can be polled from F6. This might be a difference of PV (Protocol Version), as mine returns 0x30 0x32 0x00 0x00 to command F8.

@Sonic-Amiga As I've posted before, in my case FM returns the total energy consumption (kWh) and FR returns the louver vertical position setting. When "Comfort" mode is activated, FR is always ? 0 0 0 so that may be the status flag you're looking for.

For what the other commands return, I will test and report later.

misterobotique commented 3 months ago

For my unit S22ZTES-W (ACK STX and checksum ETX omitted for the sake of clarity):

F* commands

Command Response (ASCII)
FA FFFF
FB 0000
FC 1A01
FD NAK
FE NAK
FF NAK
FG 0801
FH NAK
FI NAK
FJ NAK
FK 1001
FL 0000
FN 0000
FO NAK
FP 8300
FQ F200
FS 0000
FT 1000
FU NAK
FV NAK
FW NAK
FX NAK
FY NAK
FZ NAK

(*) For FG, as I mentioned before the second byte (8 in this case) increments by one (from 0 to F and repeat) each time the air conditioner receives a command from the remote control.

R* commands

Command Response (ASCII)
Rb 100
Rc NAK
Re 050
Rf NAK
Rh NAK
Ri NAK
Rj NAK
Rk NAK
Rl NAK
Rm NAK
Rn NAK
Ro NAK
Rp NAK
RP NAK
Rq NAK
Rr NAK
Rs NAK
Rt NAK
Ru NAK
Rv NAK
Rw NAK
Rx NAK
Ry NAK
RG A
RJ NAK
RO NAK
RQ NAK
RR NAK
RS NAK
RT NAK
RU NAK
RV NAK
RW 00
RY NAK
RZ NAK

The command Rz returns something of different format: 0x03 0x02 0x53 0x7A 0xCC 0x03 (full response).

hedgepigdaniel commented 3 months ago

What about FC command to query the model? On my units (and extrapolating to the series):

hedgepigdaniel commented 3 months ago

I found out what FR reports: the louver vertical position setting. Bytes Values Description Byte(0) G - Byte(1) R - Byte(2) ?, 0, 1, 2, 3, 4, 5 Swing, "Comfort" mode position, upper position, high middle position, middle position, low middle position, lower position Byte(3) 0
Byte(4) 0
Byte(5) 0

Hmm, my units respond as follows:

FS could be the louver horizontal position setting.

Not on mine, it's just "000" regardless of the swing settings

Also, I noticed F6 would returns 0 2 0 0 when "Night" mode is enabled (0 0 0 0 when disabled).

Not for me - also just "0000" regardless of night mode

hedgepigdaniel commented 3 months ago

FT/GT commands: Maybe something about the outdoor unit? I have

A little endian decimal or hex number referring to... the maximum number of indoor units connectable? Or just a capacity index?

hedgepigdaniel commented 3 months ago

On my units, the RG command indicates the fan speed setting, including auto and night mode. The values seem to be:

revk commented 3 months ago

That would be so nice if the case, testing on mine now.

revk commented 3 months ago

Bloody hell, that works, coding now.

hedgepigdaniel commented 3 months ago

Rb - is this some kind of load/power index? It seems to be "000" for units that are off, and also if they are on but not running. Increasing the set point in heating mode results it it increasing to "011". It does not seem to be affected by the fan speed setting, and seems distinct from the actual fan speed.

misterobotique commented 3 months ago

On my units, the RG command indicates the fan speed setting, including auto and night mode. The values seem to be:

  • "A": Auto mode
  • "B": Night/quiet mode
  • "3" through to "7": fixed fan speeds that the IR remote can set

On my unit, I got the same result but when "Night" mode is enabled RG returns A and not B like in your case. Again, I guess that's related to the difference of Protocol Version between units.

hedgepigdaniel commented 3 months ago

Re: some example values:

So then the values would be something like 60, 70, 72, 79, 79 - or if divided by 10, 6, 7, 7.2, 7.9, 7.9. Surely not temperatures, or they would change with on/off setting? If anything they decrease slightly when a unit is on. Right now the one that reports 60 is the only one that is on (heating).

Humidity sensor? Right now the weather app tells me 94% humidity, 15.8C due point, 16.8C temperature. Inside it's 20-23C probably. Makes sense.

If you are interested in a simpler calculation that gives an approximation of dew point temperature if you know the observed temperature and relative humidity, the following formula was proposed in a 2005 article by Mark G. Lawrence in the Bulletin of the American Meteorological Society:

Td = T - ((100 - RH)/5.)

https://iridl.ldeo.columbia.edu/dochelp/QA/Basic/dewpoint.html

hedgepigdaniel commented 3 months ago

On my unit, I got the same result but when "Night" mode is enabled RG returns A and not B like in your case. Again, I guess that's related to the difference of Protocol Version between units.

Makes sense. So on my unit and @revk's unit, RG indicates night mode, and on yours it's F6.

Does that align with our F8 return values? Mine is "0200".

revk commented 3 months ago

OK, strange, I built and was seeing A and B but the debug was broken, due to a change @Sonic-Amiga did. Fixing that and now I am seeing 0 in first character and an increasing number on each change in second character.

So now I have to work out why my "fix" has broken things!

OK scrap that, I was misreading, fixing the bug did not break it. I am not seeing R/S G now, which makes no sense.

I definitely was at one point, WTF.

OK R/S G is one byte, so not logged as a response in debug, fixing that lets me see it. It was showing before because the len was wrong.