Fujitsu Air Conditioning integration - Airstage dongle

Is you unit in cloud mode or local mode? I wonder if people are getting errors if their units are in cloud mode?

My unit is in cloud mode. Which is needed to get the Airco to join local wlan.
So I can ping my airco on 192.168.2.xxx and access the endpoints fine.

If I put my laptop on the airco wifi ssid and do the same rest commands (different ip offcourse) it also replys with {“result”:“NG”,“error”:“0002”}

Must be something simple I’m overlooking.

I am getting the exact same thing, I wonder if our dongles are different or require authentication. It would be surprising if it did not need the user and password.

Well since those units are in my parents house and I have no permanent testing setup I might assume some things from my previous poking.

As mentioned in AirCon readme regarding the W4A1 especially there might be some other major differences across the modules. While my modules are “WH3E” @gorstj mentioned “WJ3E”; so maybe we are stumbling over those discrepancies further more and a plugin implementation will be even more difficult as it wont be like one-fits-all.

As mentioned above the device-code is basically the mac address; also written in the ssid. I have not done detailed tests yet but it seems necessary to include all necessary fields in the request, which might be different again across the modules and capabilities of the ac. I am doing those tests with curl on linux and given the necessary data fields I had no erratic behaviour yet; it even works without any (optional?) headers.

At least in my case there is no real distinction between cloud mode and local operation as the modules are always connected to lan even while doing temporary ad-hoc mode to a client. But while I sniffed those api calls in ad-hoc mode with an android device further tests always went via lan.

@pietervanh could you verify with different or empty params? Maybe your device has a different sub-id or not especially level 2 or something.

If it helps I can confirm my module is WH3E as well, so this makes it even more interesting!

It is nice to know we don’t have to worry about cloud vs local it is another thing we can remove from the list of potential reasons. Happy to do testing here as well if I can help at all.

I am having intermittent Wireshark issue, but at one point I did get an invalid header packet back. I am going to see if I can replicate this or if it was just unrelated.

It doesn’t matter.
Basically sending the exact same command I sniffed from wshark.
Even with complete same header as the iOS app uses.

Ok I have it working, and I am actually an idiot, it was malformed JSON, so that is at least consistent with eh WH3E module. I have just got it to return the query data. Granted it is giving me temps in Fahrenheit rather than Celsius but it is indeed a start.

So I wonder if it is due to module differences that it is not working.

Well that’s weird… Not sure if there is any special provisioning going on while connecting them via Airstage the first time but in my case there was just the normal onboarding workflow.
Btw we were not able to connect them via FGLair neither on iOS nor Android due to a broken workflow. Did you previously connect them to FGLair or is some other kind of lock present? Do they have an open webserver on 80?

Never tried FGLair.
Both my Airco’s have also WH3E in the SSID name. So I presume also a WH3E module.

Oh my I got a reply. I have 2 units. And I’m blind so I mixed up the macaddresses.
Thunderclient in VS Code didn’t work but the curl command from vs code terminal on ha works.
Both my airco’s under full control now.
Now going to try to set it up with the following integrations RESTful Sensor - Home Assistant
RESTful Command - Home Assistant



sensor:
  - platform: rest
    scan_interval: 60
    name: airco_living_temp
    resource: http://aircoip/GetParams
    method: POST
    payload: '{"device_id":"deviceid","device_sub_id":0,"req_id":"","modified_by":"","set_level":"03","list":["iu_indoor_tmp"]}'
    value_template: "{{ value_json['value']['iu_indoor_tmp'] }}"
    unit_of_measurement: "°F"

rest_command:
  airco_on:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"deviceid","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_onoff":"1"}}'
  airco_off:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"deviceid","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_onoff":"0"}}'

Works great not the nicest setup but it’s late and it works for now…

The SetTemp is in Celcius * 10
The Inside and Outside Temp /100 are in Farenheit

See post below with more fullfledged example

2 Likes

Anyways you get pretty far already with standard rest integration and some scripts and conditional cards

configuration.yaml just paste at bottom and offcourse replace the AIRCOIP and AIRCOMAC in the copied text → restart HA after for configuration to take

rest_command:
  airco_bedroom_on:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_onoff":"1"}}'
  airco_bedroom_off:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_onoff":"0"}}'
  airco_bedroom_eco_on:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_economy":"1"}}'
  airco_bedroom_eco_off:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_economy":"0"}}'
  airco_bedroom_fanctrl_on:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_fan_ctrl":"1"}}'
  airco_bedroom_fanctrl_off:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_fan_ctrl":"0"}}'
  airco_bedroom_pwrfull_on:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_powerfull":"1"}}'
  airco_bedroom_pwrfull_off:
    url: http://aircoip/SetParam
    method: POST
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_powerfull":"0"}}'
  airco_bedroom_set_temp:
    url: http://aircoip/SetParam
    method: POST
    payload: >
      {% set bedroomtemp = (states('input_number.setaircotempbedroom') | float  * 10) | int %}
      {"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_set_tmp":"{{ bedroomtemp }}"}}
  airco_bedroom_setmode:
    url: http://aircoip/SetParam
    method: POST
    payload: >
      {% set aircomodes = {"Auto": 0, "Cooling": 1,"Dry": 2,"Fan": 3,"Heating": 4} %}
      {% set opmode = aircomodes[states('input_select.setaircoopmodebedroom')]  %}
      {"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"02","value":{"iu_op_mode":"{{ opmode }}"}}
  

rest:
  - resource: http://aircoip/GetParam
    scan_interval: 60
    payload: '{"device_id":"aircomac","device_sub_id":0,"req_id":"","modified_by":"","set_level":"03","list":["iu_set_tmp","iu_indoor_tmp","iu_outdoor_tmp","iu_onoff","iu_economy","iu_fan_ctrl","iu_fan_ctrl","iu_powerful","ou_low_noise"]}'
    binary_sensor:
      - name: "Airco Bedroom On"
        value_template: "{{ value_json['value']['iu_onoff'] }}"
      - name: "Airco Bedroom Economy"
        value_template: "{{ value_json['value']['iu_economy'] }}"
        icon: mdi:leaf
      - name: "Airco Bedroom Fan Control"
        value_template: "{{ value_json['value']['iu_fan_ctrl'] }}"
        icon: mdi:fan
      - name: "Airco Bedroom Powerfull"
        value_template: "{{ value_json['value']['iu_powerful'] }}"
        icon: mdi:fan-plus
      - name: "Airco Bedroom Low Noise"
        value_template: "{{ value_json['value']['ou_low_noise'] }}"
        icon: mdi:sleep
    sensor:
      - name: "Temp Outside Airco Bedroom"
        value_template: "{{ (((value_json['value']['iu_outdoor_tmp'] | float  / 100 ) - 32 ) * 5/9) | round(2) }}"
        unit_of_measurement: "°C"
      - name: "Temp Inside Airco Bedroom"
        value_template: "{{ (((value_json['value']['iu_indoor_tmp'] | float  / 100 ) - 32 ) * 5/9) | round(2) }}"
        unit_of_measurement: "°C"
      - name: "Requested Temp Airco Bedroom"
        value_template: "{{ (value_json['value']['iu_set_tmp'] | float / 10 )| round(2) }}"
        unit_of_measurement: "°C"

Scripts ( Settings > Automations & Scenes > Scripts) Add 2 scripts and paste the 2 scripts yaml

alias: Turn On Airco Bedroom
sequence:
  - service: rest_command.airco_bedroom_on
    data: {}
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: binary_sensor.airco_bedroom_on
mode: single
icon: mdi:air-conditioner

alias: Turn Off Airco Bedroom
sequence:
  - service: rest_command.airco_bedroom_off
    data: {}
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: binary_sensor.airco_bedroom_on
mode: single
icon: mdi:air-conditioner

Dashboard on / off button
image

Paste in yaml in a new manual card replace code with below

square: true
type: grid
cards:
  - type: conditional
    conditions:
      - entity: binary_sensor.airco_bedroom_on
        state: 'off'
    card:
      show_name: true
      show_icon: true
      type: button
      tap_action:
        action: call-service
        service: script.turn_on_airco_bedroom
        target: {}
      hold_action:
        action: none
      icon: mdi:air-conditioner
      name: Airco
      show_state: false
      entity: binary_sensor.airco_bedroom_on
  - type: conditional
    conditions:
      - entity: binary_sensor.airco_bedroom_on
        state: 'on'
    card:
      show_name: true
      show_icon: true
      type: button
      tap_action:
        action: call-service
        service: script.turn_off_airco_bedroom
        target: {}
      hold_action:
        action: none
      icon: mdi:air-conditioner
      name: Airco
      show_state: false
      entity: binary_sensor.airco_bedroom_on
columns: 4

Automation to update requested Temperature based on helper:
Create Helper under Settings > Devices and Entities > Helpers > Create helper


Input Helper for the dropdown to change mode

Automation , create 2 automations in gui and paste the yaml

alias: Airco bedroom temp Update
description: ""
trigger:
  - platform: state
    entity_id:
      - input_number.setaircotempbedroom
condition: []
action:
  - service: rest_command.airco_bedroom_set_temp
    data: {}
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: sensor.requested_temp_airco_bedroom
mode: single

alias: Airco bedroom mode change
description: ""
trigger:
  - platform: state
    entity_id:
      - input_select.setaircoopmodebedroom
condition: []
action:
  - service: rest_command.airco_bedroom_setmode
    data: {}
  - service: homeassistant.update_entity
    data: {}
    target:
      entity_id: sensor.requested_temp_airco_bedroom
mode: single


image

5 Likes

This is looking great, I am working on a NodeRed setup with taking HTTP POST and translating them to MQTT. I have had powerful and eco working fine here. What I am getting is a disparity between the temperatures reported by the app and what the HTTP POST is returning. Are you experiencing the same? It’s about 1 deg c out, but not sure that is consistent.

3 Likes

Can you share the requests for enabling disabling powerful mode and setting the temperature?
About temparature difference between app and post have not checked.
But the temparature from the http post seems quite in line with the other temp sensors I have in the same room.

1 Like

Sure.

The payload I use for setting the temp is.

{
        device_id:"DEVICEID",
        device_sub_id:0,
        req_id:"",
        modified_by:"",
        set_level:"02",
        value: {
                iu_set_tmp: "TEMPVAL"
            }
}

The payload for powerful is

{
        device_id:"DEVICEID",
        device_sub_id:0,
        req_id:"",
        modified_by:"",
        set_level:"02",
        value: {
                iu_powerful: "0 or 1"
            }
}

Hmm ok so maybe the reported temps are right and the app is wrong, I will measure with a probe and check.

Edit: Further thought, you can only set powerful when the aircon is on. A quirk of the system, but you can set eco if it is on or off. I suspect this is bug.

Massive progress been made here!

I will have some time Saturday evening to debug with a Mac if there are any other API commands that need checking.

Let me know a lisr

Guys, sorry for posting this somehow offtopic question. I do have 5 new ACs with airstage dongle “WH3E” as mentioned earlier. I tried to install 2 of the devices and I can get them connected to the wireless router, however the app is crashing during the cloud registration process every time on any of my 3 iOS devices. Did you encounter any such issues? I searched the internet, but I can not find any hints. Interestingly I can add a device in local mode to the app.
Actually I like to hook them all in my home assistant system. so I’m eagerly following this thread and I’m also willing to contribute as soon as I get the decives running.

Worked first time for me using the iOS app an an iPhone 14.
You can load the app on a Mac laptop (search on the app store) - maybe that will work better?

How is it crashing? Timeout? or exits?
Is it a 2.4ghz vs 5ghz wifi issue? (the dongle will only connect to 2.4ghz)

There appears something wrong with the temperature conversion

My app currently says:
Indoor: 22c
Outdoor: 16c

In HA it is saying:
Indoor: 22.36
Outdoor: 18.89

Using Curl on the command line I get:

curl -vv -X POST --data '{"device_id":"E8FB1CFF5FF5","device_sub_id":0,"req_id":"","modified_by":"","set_level":"03","list":["iu_wifi_led","iu_af_inc_hrz","iu_af_inc_vrt","iu_indoor_tmp","iu_outdoor_tmp","iu_hmn_det","iu_main_ver","iu_eep_ver","iu_has_upd_main","iu_has_upd_eep","iu_fld_set80"]}' 'http://192.168.1.187/GetParam'

{"value":{"iu_wifi_led":"65535","iu_af_inc_hrz":"0","iu_af_inc_vrt":"0","iu_indoor_tmp":"7225","iu_outdoor_tmp":"6600","iu_hmn_det":"0","iu_main_ver":"","iu_eep_ver":"","iu_has_upd_main":"65535","iu_has_upd_eep":"65535","iu_fld_set80":"65535"},"read_res":"ack","device_id":"E8FB1CFF5FF5","device_sub_id":0,"req_id":"","modified_by":"","set_level":"03","cause":"","result":"OK","error":""}

I have an iPhone 12. The crash happens directly after I press the button to start the registration after the wireless connetion. The AC stays connected with the router, but the app crashes immediatly. there is this progress bar with the 3 dots and it just is gone - no timeout, no error report. My eero router tells me it is properly connected to the nearest AP @ 2,5GHz. I’m wondering if I should try to use a different country in the setup…
I will try the app on the MAC now…

If it is connected to your wifi then try the curl command in my last post (substituting your IP address and MAC address) to see if you can interact with it without registering with the app.

If we get a decent HA integration you may not need the airstage app!