Airtouch 4 integration (AUS)

The new files are working for me, now with the main controller to run the system.

I set up a graph to look at the temps in the main rooms - is there any way to isolate out just the current temp readings? I am getting the data for the settings like the target temperature and something called “Cooling”

Hi Steve, if you look in home assistants developer tools, you should see a bunch of information when you search up your climate entities - for mine I can see current_temperature among other things - you should be able to write automations/sensors based on that info (home assistant has documentation that can explain it alot better than I would, if you just search for sensor (maybe template sensor?) And ‘state’ in general (all attributes like temperature are accessed through state in home assistant) - if you still can’t find anything feel free to message and when I’m back at my computer I can send through some links.

Sam thanks

I am still a beginner with HA, so still learning.

I can see all that data there - I was just looking for only some of it for a simple graph card on a dashboard. At the moment I am getting too much and don’t know how to filter settings out and just leave the current temp readings, so that I can see how the system has handled temps overs a few hours. So I don’t know how to extract the sensor info, and filter out the settings.

I might play around with Grafana - it looks like it might be able to do something.

@Steve61 if I’m understanding you right, that is something you should be able to achieve using HA - I am only relatively new so others might have better information… but if I make an entry in my configuration yaml under my sensor attribute (might look like this if you don’t have any sensors defined):

sensor:
  - platform: template
    sensors:
      sams_room_temperature:
        value_template: '{{states.climate.sam.attributes.current_temperature}}'

that defines a sensor named sams_room_temperature in HA that reports the value of climate.sam.current_temperature. Then I can make a lovelace Sensor Card that looks like this:

Hope that I’ve understood correctly and that helps a bit!

Looking like it does the trick exactly. I just have to wait for the “new” sensor to gather data.

Thankyou - I have learned a new thing about HA. I will be able to use the sensors from the thermostats in each room to trigger events.

Hope they integrate this soon.

Interesting that when I try to set up a graph on bedroom sensor readings it does it as a horizontal bar rather than a line, such as with the outside temp, taken from an Ecowitt weather station.

@Steve61 weird! comes up as a line graph for me:

Just had a quick search through the HA documentation - looks like you need to have a unit_of_measurement defined for a sensor in order for it to show up as a line graph: https://www.home-assistant.io/integrations/template/

1 Like

I’ll have a play. I just have to fix up some issues with HA. Either my Raspberry Pi hardware is playing up or I have a corrupted SD card or files.

I think a clean install is coming up.

You were right about the unit of measurement - as soon as I put that into the configuration yaml, it changed to a line graph. I went through and made some other changes to the template to suit.

It’s looking good.

Now that I have successfully flashed a Sonoff RF bridge with Tasmota and Portisch, I can pick up our ceiling fan commands, so I can now tie that in with the sensor in the bedroom AC thermostat (- after having a fight with the bridge, the router and my desktop. Tasmota really doesn’t like Mesh and the desktop was interfering with it.)

@LonePurpleWolf

While you airtouch integration works most of the time. It will break every now and then. I have no idea why, or when it happens. A restart of Home Assistant seems to fix it.

Any commands sent from HA get through to the unit and are actioned. But nothing gets updated on the Home assistant side of things. Instead it will throw errors such as:

Failed to call service climate/set_hvac_mode. 'AirTouchAc' object has no attribute 'ModeSupported'
Failed to call service climate/set_temperature. -1

I found this in the logs

Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 119, in _handle_refresh_interval
await self.async_refresh()
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 193, in async_refresh
update_callback()
File "/config/custom_components/airtouch4/climate.py", line 212, in _handle_coordinator_update
return super()._handle_coordinator_update()
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 245, in _handle_coordinator_update
self.async_write_ha_state()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 296, in async_write_ha_state
self._async_write_ha_state()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 314, in _async_write_ha_state
attr = self.capability_attributes
File "/usr/src/homeassistant/homeassistant/components/climate/__init__.py", line 189, in capability_attributes
self.hass, self.min_temp, self.temperature_unit, self.precision
File "/config/custom_components/airtouch4/climate.py", line 232, in min_temp
return self._airtouch.acs[self._unit.BelongsToAc].MinSetpoint
KeyError: -1

I’m not sure if that is enough to go on, I’m happy to help with more bug finding if you can point me in the right direction, I can give you more logs. This is running on HASS.IO, but I’m in the process of moving to a Ubuntu distribution running HA in a docker. Though I’m learning as I go along, so its a slow process, not sure if that will make things run any better.

It generally will run fine for a few hours at least. Then randomly stop working when I try and change things. I quick reboot and its all working fine again… so its a little strange.

Hey @mike12378912 if possible can you try and see if you still get the same behaviour on the latest version of the integration? I found similar behaviour that after a while the airtouch would send back some rubbish data and that the integration would break - it should be slightly more resilient now. If you’re still having issues let me know and we can try and dig into it more

OK, not worries. I didn’t realise you updated it… will let you know.

Seems to be pretty stable now with that update. Haven’t had to restart it in the last couple of days.

Does anyone know how to make custom cards for home assistant? Having 7 zones, and 8 cards, if it could be consolidated into a tidy list… a small slider for temperature instead of the big thermostat, simple on/off for the zones, and no temperature slider for the main air con etc would really be the icing on the cake for this.

Hi mike, I use custom button card and slider entity row for this sort of thing. Here is some example code I put together to get you started.

You can change the background colour based on the state of the system and easily expand the 4 rows of 2 columns for your 7 zones and master. Very customisable. I hope this helps.

custom_fields:
  slider:
    card:
      entity: climate.master
      full_row: true
      hide_state: false
      min: 1
      show_icon: false
      type: 'custom:slider-entity-row'
  master: |
    [[[
         return `<ha-icon
            icon="mdi:fan"
            style="width: 25px; height: 25px; color: deepskyblue;">
            </ha-icon><span> <span style="color: var(--text-color-sensor);">Master - ${states['climate.master'].attributes['current_temperature']}  °C</span></span>`
    ]]]
  living: |
    [[[
         return `<ha-icon
            icon="mdi:fan"
            style="width: 25px; height: 25px; color: deepskyblue;">
            </ha-icon><span> <span style="color: var(--text-color-sensor);">Living - ${states['climate.ac'].attributes['current_temperature']}  °C</span></span>`
    ]]]
entity: climate.master
name: Airtouch System
styles:
  card:
    - background-color: |
        [[[
          if (states["climate.master"].state != "off") return 'grey';
          else return 'var(--primary-background-color)';
        ]]]
    - padding: 2%
    - font-size: 15px
  custom_fields:
    master:
      - align-self: middle
      - justify-self: start
    living:

      - align-self: middle
      - justify-self: start
    slider:
      - align-self: middle
      - justify-self: start
      - width: 100%
  grid:
    - grid-template-areas: '"n n" "master living" "slider slider"'
    - grid-template-rows: min-content min-content min-content min-content
    - grid-template-columns: 1fr 1fr
  img_cell:
    - justify-content: start
    - align-items: start
    - margin: none
  name:
    - font-weight: bold
    - font-size: 20px
    - align-self: middle
    - justify-self: start
    - padding-bottom: 5px
show_icon: false
tap_action:
  action: more-info
type: 'custom:button-card'```

Thanks for the pointer in the right direction James… it was super helpful. After a few hours of trial and error I’m getting to something that I find functional, however it doesn’t look fantastic… i can’t seem to make it look nice, the spacing and alignment is all wrong. The image is the actual size of the card…

I do however have the ability to turn off and on each zone, and set temperature… and its fairly compact. I still have to get the main system on it as well, which looks really diffcult … so I think that is going to be another few days of tinkering. Hopefully I’ll have half a clue what i’m doing by the end… However I paste my horrible code if anyone wants to have a crack, or can help me make it look better. I’m sure there is a better way of writing the card… but I’m just running with what James started me on.

edit: Code superseded by new post.

I played around a bit more today, and got to this point. I’m pretty happy with it right now, it can be improved… But I have all the functionally (except fan speed which I think isn’t implemented in the integration yet) in a much smaller footprint.

Because its so much smaller now… i think instead of a slider, I’d prefer just a box with up and down arrows to change the temperature… i can’t seem to find the name of that though, so I can’t find the custom card!

Main Controls

type: 'custom:button-card'
custom_fields:
  titlelabel: 'Airtouch 4:'
  titlemode: '[[[ return `${states["climate.ac0"].state}` ]]] '
  ac0auto:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].state != "auto") return 'grey';
          else return 'darkgreen';
        ]]]      
      icon: 'mdi:brightness-auto'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_hvac_mode
        service_data:
          entity_id: climate.ac0
          hvac_mode: auto
  ac0heat:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].state != "heat") return 'grey';
          else return 'darkgreen';
        ]]]          
      icon: 'mdi:white-balance-sunny'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_hvac_mode
        service_data:
          entity_id: climate.ac0
          hvac_mode: heat
  ac0cool:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].state != "cool") return 'grey';
          else return 'darkgreen';
        ]]]         
      icon: 'mdi:air-conditioner'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_hvac_mode
        service_data:
          entity_id: climate.ac0
          hvac_mode: cool
  ac0dry:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].state != "dry") return 'grey';
          else return 'darkgreen';
        ]]]              
      icon: 'mdi:hair-dryer'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_hvac_mode
        service_data:
          entity_id: climate.ac0
          hvac_mode: dry
  ac0fan:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].state != "fan_only") return 'grey';
          else return 'darkgreen';
        ]]]       
      icon: 'mdi:fan'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_hvac_mode
        service_data:
          entity_id: climate.ac0
          hvac_mode: fan_only
  ac0power:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: darkgreen
      icon: 'mdi:power'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: red
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.ac0"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.ac0
  ac0fanauto:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].attributes.fan_mode != "auto") return 'grey';
          else return 'darkgreen';
        ]]]       
      icon: 'mdi:fan'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_fan_mode
        service_data:
          entity_id: climate.ac0
          fan_mode: auto
  ac0fanlow:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].attributes.fan_mode != "low") return 'grey';
          else return 'darkgreen';
        ]]]       
      icon: 'mdi:fan-speed-1'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_fan_mode
        service_data:
          entity_id: climate.ac0
          fan_mode: low
  ac0fanmedium:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].attributes.fan_mode != "medium") return 'grey';
          else return 'darkgreen';
        ]]]       
      icon: 'mdi:fan-speed-2'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_fan_mode
        service_data:
          entity_id: climate.ac0
          fan_mode: medium
  ac0fanhigh:
    card:
      entity: climate.ac0
      show_state: false
      show_name: false
      show_icon: true
      color: |
        [[[
          if (states["climate.ac0"].attributes.fan_mode != "high") return 'grey';
          else return 'darkgreen';
        ]]]       
      icon: 'mdi:fan-speed-3'
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: climate.set_fan_mode
        service_data:
          entity_id: climate.ac0
          fan_mode: high
styles:
  card:
    - padding: 2%
    - font-size: 15px
  custom_fields:
    titlelabel:
      - align-self: middle
      - justify-self: start
    titlemode:
      - align-self: middle
      - justify-self: start
    ac0auto: null
    ac0heat: null
    ac0cool: null
    ac0dry: null
    ac0fan: null
    ac0power: null
    ac0fanauto: null
    ac0fanlow: null
    ac0fanmedium: null
    ac0fanhigh: null
  grid:
    - grid-template-areas: >-
        "titlelabel titlelabel titlelabel titlemode titlemode titlemode" "l l l
        l l l" "ac0auto ac0heat ac0cool ac0dry ac0fan ac0power" "ac0fanauto
        ac0fanlow ac0fanmedium ac0fanhigh i i"
    - grid-template-rows: 1fr 1fr 1fr 1fr
    - grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr
  img_cell:
    - justify-content: start
    - align-items: start
    - margin: none
  name:
    - font-weight: bold
    - font-size: 20px
    - align-self: middle
    - justify-self: start
    - padding-bottom: 5px
show_icon: false

Zones

type: 'custom:button-card'
custom_fields:
  column1: Zone
  column2: Current
  column3: Set
  column4: On/Off
  masterlabel: Master
  bed2label: Bed 2
  bed3label: Bed 3
  bed4label: Bed 4
  familylabel: Family
  garagelabel: Garage
  medialabel: Media
  mastertemp: >-
    [[[ return `${states["climate.master"].attributes.current_temperature} °C`
    ]]]
  bed2temp: >-
    [[[ return `${states["climate.bed_2"].attributes.current_temperature} °C`
    ]]]
  bed3temp: >-
    [[[ return `${states["climate.bed_3"].attributes.current_temperature} °C`
    ]]]
  bed4temp: >-
    [[[ return `${states["climate.bed_4"].attributes.current_temperature} °C`
    ]]]
  familytemp: >-
    [[[ return `${states["climate.family"].attributes.current_temperature} °C`
    ]]]
  garagetemp: >-
    [[[ return `${states["climate.garage"].attributes.current_temperature} °C`
    ]]]
  mediatemp: >-
    [[[ return `${states["climate.media"].attributes.current_temperature} °C`
    ]]]
  masterslider:
    card:
      entity: climate.master
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  bed2slider:
    card:
      entity: climate.bed_2
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  bed3slider:
    card:
      entity: climate.bed_3
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  bed4slider:
    card:
      entity: climate.bed_4
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  familyslider:
    card:
      entity: climate.family
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  garageslider:
    card:
      entity: climate.garage
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  mediaslider:
    card:
      entity: climate.media
      full_row: true
      hide_state: false
      min: 16
      max: 30
      show_icon: false
      type: 'custom:slider-entity-row'
  masterbutton:
    card:
      entity: climate.master
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      right: 10px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.master"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.master
  bed2button:
    card:
      entity: climate.bed_2
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.bed_2"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.bed_2
  bed3button:
    card:
      entity: climate.bed_3
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.bed_3"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.bed_3
  bed4button:
    card:
      entity: climate.bed_4
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.bed_4"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.bed_4
  familybutton:
    card:
      entity: climate.family
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.family"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.family
  garagebutton:
    card:
      entity: climate.garage
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.garage"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.garage
  mediabutton:
    card:
      entity: climate.media
      show_state: false
      show_name: false
      show_icon: true
      icon: 'mdi:power'
      color: darkgreen
      size: 25px
      type: 'custom:button-card'
      state:
        - value: 'off'
          color: grey
      tap_action:
        action: call-service
        service: >-
          [[[ if (states["climate.media"].state != "off") return
          "climate.turn_off"; else return "climate.turn_on" ]]]
        service_data:
          entity_id: climate.media
styles:
  card:
    - padding: 2%
    - font-size: 15px
  custom_fields:
    column1:
      - justify-self: start
      - padding-left: 30%
    column2:
      - justify-self: start
      - padding-left: 30%
    column3: null
    column4: null
    masterlabel:
      - justify-self: start
      - padding-left: 30%
    bed2label:
      - justify-self: start
      - padding-left: 30%
    bed3label:
      - justify-self: left
      - padding-left: 30%
    bed4label:
      - justify-self: left
      - padding-left: 30%
    familylabel:
      - justify-self: left
      - padding-left: 30%
    garagelabel:
      - justify-self: left
      - padding-left: 30%
    medialabel:
      - justify-self: left
      - padding-left: 30%
    mastertemp:
      - justify-self: left
      - padding-left: 30%
    bed2temp:
      - justify-self: start
      - padding-left: 30%
    bed3temp:
      - justify-self: start
      - padding-left: 30%
    bed4temp:
      - justify-self: start
      - padding-left: 30%
    familytemp:
      - justify-self: start
      - padding-left: 30%
    garagetemp:
      - justify-self: start
      - padding-left: 30%
    mediatemp:
      - justify-self: start
      - padding-left: 30%
    masterslider: null
    bed2slider: null
    bed3slider: null
    bed4slider: null
    familyslider: null
    garageslider: null
    mediaslider: null
    masterbutton: 
      - padding-left: 25%
      - padding-right: 25%    
    bed2button: 
      - padding-left: 25%
      - padding-right: 25%    
    bed3button: 
      - padding-left: 25%
      - padding-right: 25%    
    bed4button: 
      - padding-left: 25%
      - padding-right: 25%    
    familybutton: 
      - padding-left: 25%
      - padding-right: 25% 
    garagebutton:
      - padding-left: 25%
      - padding-right: 25%
    mediabutton:
      - padding-left: 25%
      - padding-right: 25%
  grid:
    - grid-template-areas: >-
        "column1 column2 column3 column4" "masterlabel mastertemp masterslider
        masterbutton" "bed2label bed2temp bed2slider bed2button" "bed3label
        bed3temp bed3slider bed3button" "bed4label bed4temp bed4slider
        bed4button" "familylabel familytemp familyslider familybutton"
        "garagelabel garagetemp garageslider garagebutton" "medialabel mediatemp
        mediaslider mediabutton" 
    - grid-template-rows: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
    - grid-template-columns: 1fr 1fr 1fr 1fr
  img_cell:
    - justify-content: start
    - align-items: start
    - margin: none
show_icon: false

edit: Worked out how to write labels… I’m an idiot! Cleaned it up a little
edit2: added fan modes + change some formatting

1 Like

looks great! Fan speed is definitely implemented in the integration! (just no zone percentages)fanspeed

You were correct, it was working… I just made some mistakes the first time and thought it wasn’t… I have updated my post to add in the fan modes.

Hi all, newby here.

I have read through this a couple time and attempted it over a couple days but keep coming up with different issues. When looking through the components folder of the repo, there seems to be tons of other addons I would not require?

To confirm:
Am I supposed to copy every file from https://github.com/LonePurpleWolf/core/tree/airtouch4-integration-2 and put it into config folder within my HASS setup? Or just certain sections?

Thank you in advance for any assistance :slight_smile: