Polyaire Air Touch 2 or 3

Hey mate, how’d you go with the airtouch 2? Did you successfully retrofit the AT3 integration?

1 Like

Gents, i’m having a crack at this on my AirTouch 2 and coming up with some interesting stuff.

So far, if you hook up TerraTerm to the control interface unit (the HF-11 unit) and connect to port 8899, press shift and escape twice (puts it in hexadecimal mode) you will get a response when you log in with a remote client app (to let the display know what the status is), I then clear the screen and press a single button on the app, cut and paste the hex into a text editor, clear the screen again, press the same button(like power to toggle between on and off, or temperature up, or zone percentage) then cut and paste that return hex code into the same file.

I’ve imported that text into excel and then used the conditional formatting to isolate what values have changed between the 2 sets of data

So far I have isolated the Power status, Zone1-8 percentage, Set Point temperature, current temperature values, system name, service phone number, system serial number, gonna work on a few more then it’s figuring out which value determines if you are setting a value for the controller or returning a status to a client app or touchscreen.

From what I can tell the bulk of the message remains the same and it’s only a few handfulls of values that change and all these are at the back end of the returned status message. What this means is that it’ll be much easier to template the values from and controls to and from the device

2 Likes

Here’s what I have decoded so far, from what i have read for the airtouch4 protocols it’ll be a couple of bytes inthe header that determins if it’s an info\status message or a command message, but the command messages could be broken up targeting specific byes denoting a fan speed command or a zone command, anyway at least with this we can start looking at a nodeRED flow to decode and update a sensor so values can be displayed in HA.

Updates on decoding the output (probably the most useful info so far)

To make it more useful i have started to integrate into MQTT using nodeRED.

You’ll probably need to re-configure your MQTT in\out nodes inthei flow to match your settings on your network. (the code posts as a single line as that’s how it exported from nodeRED)

[{"id":"58b5e101.08975","type":"tab","label":"AirTouch","disabled":false,"info":""},{"id":"1bb819ab.902fa6","type":"tcp in","z":"58b5e101.08975","name":"","server":"client","host":"CONTROLLER_IP_ADDRESS","port":"8899","datamode":"stream","datatype":"buffer","newline":"","topic":"","base64":false,"x":220,"y":120,"wires":[["c5afa6a8.a9cb38","d094db5e.852fa8"]]},{"id":"d7a24eba.95206","type":"debug","z":"58b5e101.08975","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":670,"y":180,"wires":[]},{"id":"d094db5e.852fa8","type":"function","z":"58b5e101.08975","name":"AC Status","func":"var newMsg = \"\"\nvar mqttMessage = \"\"\nvar sysStatus = \"\"\n\nif (msg.payload[354] > 10){\n    mqttMessage = {payload:\"ON\", topic:\"tele/AirCon/Power\"};\n} else {\n    mqttMessage = {payload:\"OFF\", topic:\"tele/AirCon/Power\"};\n}\nnode.send(mqttMessage,false);\nnode.done();\n\nswitch(msg.payload[358]) {\n    case 0:\n        mqttMessage = {payload:\"auto\", topic:\"tele/AirCon/Mode\"};\n    break;\n    case 1:\n        mqttMessage = {payload:\"heat\", topic:\"tele/AirCon/Mode\"};\n    break;\n    case 2:\n        mqttMessage = {payload:\"dry\", topic:\"tele/AirCon/Mode\"};\n    break;\n    case 3:\n        mqttMessage = {payload:\"fan_only\", topic:\"tele/AirCon/Mode\"};\n    break;\n    case 4:\n        mqttMessage = {payload:\"cool\", topic:\"tele/AirCon/Mode\"};\n    break\n}\nnode.send(mqttMessage,false)\nnode.done();\n\nmqttMessage = {payload:msg.payload[362], topic:\"tele/AirCon/SetPoint\"}\nnode.send(mqttMessage,false);\nnode.done();\nmqttMessage = {payload:msg.payload[364], topic:\"tele/AirCon/CurrentTemp\"}\nnode.send(mqttMessage,false);\nnode.done();\n\nswitch(msg.payload[360]) {\n    case 48:\n        mqttMessage = {payload:\"Auto\", topic:\"tele/AirCon/FanSpeed\"};\n    break;\n    case 49:\n        mqttMessage = {payload:\"Low\", topic:\"tele/AirCon/FanSpeed\"};\n    break;\n    case 50:\n        mqttMessage = {payload:\"Medium\", topic:\"tele/AirCon/FanSpeed\"};\n    break;\n    case 51:\n        mqttMessage = {payload:\"High\", topic:\"tele/AirCon/FanSpeed\"};\n    break;\n}\nnode.send(mqttMessage,false);\nnode.done();\nreturn;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":440,"y":180,"wires":[["f740d5ae.191468","d7a24eba.95206"]]},{"id":"c5afa6a8.a9cb38","type":"mqtt out","z":"58b5e101.08975","name":"AC Raw Data","topic":"tele/AirCon/rawmessage","qos":"","retain":"","broker":"5778013a.241c4","x":460,"y":60,"wires":[]},{"id":"f740d5ae.191468","type":"mqtt out","z":"58b5e101.08975","name":"ACMQTTDecoded","topic":"","qos":"","retain":"","broker":"5778013a.241c4","x":690,"y":120,"wires":[]},{"id":"5778013a.241c4","type":"mqtt-broker","name":"HAssMQTT","broker":"MQTT_SERVER_IP_ADDRESS","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""}]

The above flow should do a few things, it’ll connect to the port 8899 on the controller and decode a few items. In the code of the function you can see how to reference the array ID’s as decimal type index numbers from my previous posting (msg.payload(XXX) where XXX is the decimal number of the item you wish to read).

The switch statements and if’s in the function just help to translate and present the info nicely should it need it. To read out the names of the zones or other multi-byte fields you will need to use some loops to combine these entries into a string which you can then output to and MQTT topic which can be picked up by an HA MQTT sensor.

From there you can use the MQTT Climate feature natively in HA (https://www.home-assistant.io/integrations/climate.mqtt/) to present the info in a card, it’ll do basic central system stuff but for a zoned ducted system like i have then separate entities may be needed to control the indiviual air outputs (Work in progress)

I have taken the easy (lazy?) way out. I used Assistant Relay to control my Airtouch 2. I assume it’ll work with any Google integrated AC system. The guide is a little outdated and it’s not maintained anymore but it still working.

It’s not locally controlled as you need Google, buts it’s the best I could manage until someone way smarter than me produces a better solution.

The only other thing I have besides the AT2 is Aqara temp sensors in the bedrooms. My built in AC unit temp monitor is in the living area so no good for maintaining temperature overnight in the bedrooms.

First I created 8 input.booleans, 6 zones and then more 2 for switching to cooling and heating that I wanted to control, like this:

cards:
  - type: button
    tap_action:
      action: toggle
    entity: input_boolean.air_con
    show_name: true
    name: Cooling
  - type: button
    tap_action:
      action: toggle
    entity: input_boolean.heating
    show_name: true
    name: Heating
  - type: button
    tap_action:
      action: toggle
    entity: input_boolean.master_bed_aircon
    show_name: true
	name: Master Bed Aircon

Then I set up Automations to call the Assistant relay service when the input.booleans are toggled, like this:

alias: AC - Group 3 On (Master bed)
description: send relay to google assistant to turn the AC on
trigger:
  - platform: state
    entity_id: input_boolean.master_bed_aircon
    from: 'off'
    to: 'on'
condition: []
action:
  - data:
      command: turn on Group 3
    service: rest_command.assistant_relay
mode: single

alias: AC - Group 3 Off (Master Bed)
description: send relay to google assistant to turn the AC off
trigger:
  - platform: state
    entity_id: input_boolean.master_bed_aircon
    from: 'on'
    to: 'off'
condition: []
action:
  - data:
      command: turn off Group 3
    service: rest_command.assistant_relay
mode: single

So now I can switch on the AC unit to cooling or heating and turn on and off each group through HA.

And finally to make it a little smart, some more automations to switch the groups on and off to maintain temperature. Like this:

alias: AC - Group 3 Auto On (Master Bed)
description: ''
trigger:
  - platform: numeric_state
    entity_id: sensor.master_bed_temp
    below: '20'
condition: []
action:
  - service: input_boolean.turn_on
    target:
      entity_id: input_boolean.master_bed_aircon
mode: single

alias: AC - Group 3 Auto Off (Master Bed)
description: ''
trigger:
  - platform: numeric_state
    entity_id: sensor.master_bed_temp
    above: '23'
condition: []
action:
  - service: input_boolean.turn_off
    target:
      entity_id: input_boolean.master_bed_aircon
mode: single

You could possible do it using a script in a more succinct way but I find this way easy to understand and debug.

1 Like

Hi All,

I just want to say that I’m super happy with Mike’s solution (@ozczecho). His code worked straight away and his doco was really good. As I have an AT3, I didn’t need to modify this at all. As I already use docker, this is the perfect solution for me.

I was able to add this to HomeKit straight from Home Assistant as the aircon is treated as a climate entity and the zones are treated as switches.

The downside is that temperature can only go up and down in increments of 1. This means the official thermostat using a slider can only go up and down by 1 degree at a time. This is not a big deal as the official app is also limited to increments of 1.

Also I don’t have temperature sensors with the AT3, I have just been setting the open % of zones using the airtouch app and then setting on/off automation using Home Assistant.

Thanks again for your help Mike, great work!

Just for anyone else that may be reading this, I had trouble using the rs485 port of the AT3 to control the aircon. This seemed to work as Home assistant was able to read from the port but I realised that the AT3 was actually already communicating with the aricon through this port. As there is only one master in modbus, there was no way to control the actual AT3 using this method.

Hi all,

Today I got an AirTouch2+ installed, I installed the ozczecho custom_component, but I am unable to pass the setup stage in the UI.

I did a quick NMAP of the controller, and I can only see port 9200 opened, no signs of 8899.

Port 8899

2022-01-28 10:52:38 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 986, in _wrap_create_connection
    return await self._loop.create_connection(*args, **kwargs)  # type: ignore[return-value]  # noqa
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1056, in create_connection
    raise exceptions[0]
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 1041, in create_connection
    sock = await self._connect_sock(
  File "/usr/local/lib/python3.9/asyncio/base_events.py", line 955, in _connect_sock
    await self.sock_connect(sock, address)
  File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 502, in sock_connect
    return await fut
  File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 537, in _sock_connect_cb
    raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 111] Connect call failed ('192.168.0.86', 8899)
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/config/custom_components/airtouch3/config_flow.py", line 49, in async_step_user
    await device.async_update()
  File "/config/custom_components/airtouch3/vzduch.py", line 139, in async_update
    response = await self.prep_fetch(HTTP_GET, GET_VZDUCH_INFO)
  File "/config/custom_components/airtouch3/vzduch.py", line 118, in prep_fetch
    return await self.fetch_get(command)
  File "/config/custom_components/airtouch3/vzduch.py", line 88, in fetch_get
    async with self._session.get("{base_url}{command}".format(
  File "/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 1138, in __aenter__
    self._resp = await self._coro
  File "/usr/local/lib/python3.9/site-packages/aiohttp/client.py", line 535, in _request
    conn = await self._connector.connect(
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 542, in connect
    proto = await self._create_connection(req, traces, timeout)
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 907, in _create_connection
    _, proto = await self._create_direct_connection(req, traces, timeout)
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 1206, in _create_direct_connection
    raise last_exc
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 1175, in _create_direct_connection
    transp, proto = await self._wrap_create_connection(
  File "/usr/local/lib/python3.9/site-packages/aiohttp/connector.py", line 992, in _wrap_create_connection
    raise client_error(req.connection_key, exc) from exc
aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host 192.168.0.86:8899 ssl:default [Connect call failed ('192.168.0.86', 8899)]
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/aiohttp/web_protocol.py", line 435, in _handle_request
    resp = await request_handler(request)
  File "/usr/local/lib/python3.9/site-packages/aiohttp/web_app.py", line 504, in _handle
    resp = await handler(request)
  File "/usr/local/lib/python3.9/site-packages/aiohttp/web_middlewares.py", line 117, in impl
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/security_filter.py", line 60, in security_filter_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/forwarded.py", line 98, in forwarded_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/request_context.py", line 28, in request_context_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/ban.py", line 78, in ban_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/auth.py", line 181, in auth_middleware
    return await handler(request)
  File "/usr/src/homeassistant/homeassistant/components/http/view.py", line 137, in handle
    result = await result
  File "/usr/src/homeassistant/homeassistant/components/config/config_entries.py", line 157, in post
    return await super().post(request, flow_id)
  File "/usr/src/homeassistant/homeassistant/components/http/data_validator.py", line 62, in wrapper
    result = await method(view, request, *args, **kwargs)
  File "/usr/src/homeassistant/homeassistant/helpers/data_entry_flow.py", line 110, in post
    result = await self._flow_mgr.async_configure(flow_id, data)
  File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 252, in async_configure
    result = await self._async_handle_step(flow, cur_step["step_id"], user_input)
  File "/usr/src/homeassistant/homeassistant/data_entry_flow.py", line 325, in _async_handle_step
    result: FlowResult = await getattr(flow, method)(user_input)
  File "/config/custom_components/airtouch3/config_flow.py", line 56, in async_step_user
    except web_exceptions.HTTPForbidden:
NameError: name 'web_exceptions' is not defined

Port 9200

  File "/config/custom_components/airtouch3/config_flow.py", line 56, in async_step_user
    except web_exceptions.HTTPForbidden:

I also ran vzduch-dotek, and it returns nothing with port 8899 and just blank values when using 9200

	
aircons	
0	
id	0
airTouchId	"00000000"
mode	0
name	""
powerStatus	0
status	"OK"
brandId	0
touchPadGroupId	0
touchPadTemperature	0
desiredTemperature	0
roomTemperature	0
thermostatMode	0
fanMode	5
zones	[]
sensors	
0	
id	0
isAvailable	false
isLowBattery	false
temperature	0
1	
id	1
isAvailable	false
isLowBattery	false
temperature	0
2	
id	2
isAvailable	false
isLowBattery	false
temperature	0
3	
id	3
isAvailable	false
isLowBattery	false
temperature	0
4	
id	4
isAvailable	false
isLowBattery	false
temperature	0
5	
id	5
isAvailable	false
isLowBattery	false
temperature	0
6	
id	6
isAvailable	false
isLowBattery	false
temperature	0
7	
id	7
isAvailable	false
isLowBattery	false
temperature	0
8	
id	8
isAvailable	false
isLowBattery	false
temperature	0
9	
id	9
isAvailable	false
isLowBattery	false
temperature	0
10	
id	10
isAvailable	false
isLowBattery	false
temperature	0
11	
id	11
isAvailable	false
isLowBattery	false
temperature	0
12	
id	12
isAvailable	false
isLowBattery	false
temperature	0
13	
id	13
isAvailable	false
isLowBattery	false
temperature	0
14	
id	14
isAvailable	false
isLowBattery	false
temperature	0
15	
id	15
isAvailable	false
isLowBattery	false
temperature	0
16	
id	16
isAvailable	false
isLowBattery	false
temperature	0
17	
id	17
isAvailable	false
isLowBattery	false
temperature	0
18	
id	18
isAvailable	false
isLowBattery	false
temperature	0
19	
id	19
isAvailable	false
isLowBattery	false
temperature	0
20	
id	20
isAvailable	false
isLowBattery	false
temperature	0
21	
id	21
isAvailable	false
isLowBattery	false
temperature	0
22	
id	22
isAvailable	false
isLowBattery	false
temperature	0
23	
id	23
isAvailable	false
isLowBattery	false
temperature	0
24	
id	24
isAvailable	false
isLowBattery	false
temperature	0
25	
id	25
isAvailable	false
isLowBattery	false
temperature	0
26	
id	26
isAvailable	false
isLowBattery	false
temperature	0
27	
id	27
isAvailable	false
isLowBattery	false
temperature	0
28	
id	28
isAvailable	false
isLowBattery	false
temperature	0
29	
id	29
isAvailable	false
isLowBattery	false
temperature	0
30	
id	30
isAvailable	false
isLowBattery	false
temperature	0
31	
id	31
isAvailable	false
isLowBattery	false
temperature	0
version	"0.4"
selectedAc	0

Any idea where I should look next before diving deep into IFTTT?

@Redndh - The custom_component + vzduch-dotek is only tested against Airtouch3. Never tested Airtouch2+ . I think they have different internals.

1 Like

I never got it working on my AT2, don’t think it’s a AT2+. The relay assistant I posted a few posts up is still working for me

Is the unit exposed as a climate entity with relay assistant?

No, relay assistant just emulates voice commands to Google Home. So any command GH can do, AR can do.

You need to link your AT2 to GH first of course

Aha! Thanks for the tip, no GH here unfortunately. Alexa will have to do in the meantime.

I really hope that someone integrates AirTouch2+ at some point. I tried integrating using IFTTT this weekend, but the results were pretty disappointing. Only a bunch of buttons executing commands, with no means of pulling any data from IFTTT to check the status of the air con :frowning: Not to mention that if you want to have more than 5 applets, you need to start paying IFTTT, the AirTouch integration offers 0 way of doing variables aside from setting the temperature, so you need one applet for each command (set fan to high, set fan to medium, set to cool, etc.)

Me to I found the api here https://github.com/ozczecho/vzduch-dotek/files/5699201/AirTouch.2%2B.Communication.Protocol.V1.0.1.pdf but it’s a bit beyond me :frowning:

Good find @Selmak!

How does one go about writing an integration with the new found API?

Wished I knew how. I asked a year ago when I got the api.

I’m going to give it a shot this week, see if I can write a bit of Python to handle the sock requests. Shall keep you posted if I managed to get something working

Awesome. good luck :smiley: if you need someone to test anything I can help.

1 Like

I can have a look and test too if needed

1 Like

@Redndh let me know if you need help with this.
I might see if I can whip up a simple python client based on the communication protocol. I don’t have a unit to test against though…

2 Likes