ESI Controls Thermostat Integration

Would be great to see an ESI Controls integration to enable full control of their WiFi thermostats via a thermostat card.
ESI Controls currently have the ESI Centro app and an integration with this must be possible via their API.
As a growing number of new build homes in the UK are being fitted with these ESI WiFi thermostats an integration with HA would be welcomed by a growing many.

Totally agree, I have these as well and would love an integration. Unfortunately I’m not sure they have open APIs available.

Im no developer but have been tinkering with this for some time, I currently have homeassistant pulling the current set temperature for the 2 esi thermostats in my house. I am also able to set the temperature on each thermostat but I dont currently use this as it needs some more work.

The API seems very simple with only a few URLs used for status and for setting temperatures. I believe there will be more related to schedules but ive not gone down that route.

The main challenge is the initial authentication which I cannot do yet from HA. Once authenticated, a token is generated, this token seems to be very long lived, I have only needed to update the token in HA once in 2 years. This may change so trying to work out a method for HA to authenticate like the app.

Happy to share what I have done so far, bear in mind the biggest challenge will be getting the authentication token for your account.

3 Likes

Hey, @P3nf0ld , i’d love you know how you managed this, as I’ve been looking to integrate my ESI Thermostats to HA for ages.

Please share. not put off by doing some manual work outside of HA

Cheers

Also interested

@P3nf0ld Would love to see what you have running so far. I did find a repo on Github which claims to support login, however I haven’t been able to get it to work

1 Like

I have the ESI series 6, currently using it through Alexa, it also works with the ESI Centro app. I really like the thermostat, and the centro app isn’t bad. Both really easy to use for the whole family, and the stat itself looks nice. However I’d love to integrate it into Home assistant as we move further from cloud reliance (or at least Alexa reliance) and transition to local.

I too found that ESI Python API client, and wondered if it would come in useful for integrating it into Home Assistant, but as I’m a complete newcomer to home assistant, with very little coding knowledge, I was unable to do anything with it. Assuming that python code is legit, does that mean it should be easy for someone with the skills to integrate it into Home Assistant?

I would love to receive this too if you don’t mind.
I have little coding knowledge but happy to have a play around and use my coder friends to see if we can progress too.

Sorry for the delay in responding, happy to share what I know.

The information I have comes from analysing the traffic from the ESi Centro app but the shared python repository shared by @swestcott pretty much has the same info. The python library likely does not work anymore as the app now sends the password encrypted/hashed which the python code does not do.

This may become a long-winded post so apologise in advance :slight_smile:

Login
At present, I have not been able to get this working through HA but have captured the API calls from the app so if you are able to capture the encrypted password value, you can make a call to the login url;

https://esiheating.uksouth.cloudapp.azure.com/centro/login

The call needs to be of type application/x-www-form-urlencoded along with a minimum of 3 values;

email - The email used to set up the account (url encoded)
password - Encrypted/hashed password (url encoded)
password_encryption - default value is password_encryption

Below is an example call using Curl;

curl -X POST -d "email=not.my%40email.com&password=EncryptedPassword&password_encryption=password_encryption&" https://esiheating.uksouth.cloudapp.azure.com/centro/login

If successful, a json formatted string will be returned in the result, the 2 key values from the string are;

id - id assigned to your account (probably numeric), this is not the username on the account.
token - long lived session token

These values will be used for authentication for all other ESi API calls

Device Info
Once you have the id and token, calls to the following URL will return data on all your configured ESI devices;

https://esiheating.uksouth.cloudapp.azure.com/centro/getSpecifyDeviceList

The call needs to be of type application/x-www-form-urlencoded along with a minimum of 3 values;

user_id - The id returned from login
token - The token value returned from the login
device_type - comma separated list of device types 02,04,10,20,23,25 (url encoded)

Based on the api call, additional values can be passed, I assume this is for reducing and filtering the list of results, the defaults sent from the app are;

startIndex=0
totalSize=12
online=1

Again, this can be tested using Curl;

curl -X POST -d "user_id=00000&device_type=02%2C04%2C10%2C20%2C23%2C25&token=YourTokenValue https://esiheating.uksouth.cloudapp.azure.com/centro/getSpecifyDeviceList

When successful, a json formatted string is returned for all of your ESi devices, in my configuration, I am parsing the following values from the json;

device_id - Id of device, this will be needed to set values
device_name - The name of the device as shown in the app
inside_temparature - The actual temperature at the device
current_temprature - The set target temperature of the device
online - is the device online or not (true/false)
upgrade - is there an upgrade available for the device (true/false)

The temperature values are 3 digits but without decimals so 19.5 is sent as 195, 19.0 as 190 etc.

In HA, I am using this via a rest api call configured in the configuration.yaml;

rest:
  - resource: https://esiheating.uksouth.cloudapp.azure.com/centro/getSpecifyDeviceList
    method: POST
    verify_ssl: false
    headers:
      Content-Type: "application/x-www-form-urlencoded"
    payload: !secret esi_get_payload
    scan_interval: 60
    sensor:
      - name: "ESI_Downstairs"
        json_attributes_path: "$.devices.0"
        value_template: "OK"
        json_attributes:
          - "device_id"
          - "device_name"
          - "inside_temparature"
          - "current_temprature"
          - "online"
          - "upgrade"
      - name: "ESI_Upstairs"
        json_attributes_path: "$.devices.1"
        value_template: "OK"
        json_attributes:
          - "device_id"
          - "device_name"
          - "inside_temparature"
          - "current_temprature"
          - "online"
          - "upgrade"

The payload is stored in my secrets.yaml;

esi_get_payload: "startIndex=0&totalSize=12&user_id=0000&online=1&device_type=02%2C04%2C10%2C20%2C23%2C25&token=YourTokenValue"

To convert the temperatures to useable values, I also have sensor templates configured in the configuration.yaml;

  - platform: template
    sensors:
      upstairs_current_temperature:
        value_template: "{{ (state_attr('sensor.ESI_Upstairs', 'inside_temparature')|int(0))/10}}"
        friendly_name: "Upstairs Temperature"
        unit_of_measurement: "°C"
      upstairs_set_temperature:
        value_template: "{{ (state_attr('sensor.ESI_Upstairs', 'current_temprature')|int(0))/10}}"
        friendly_name: "Upstairs Set Temperature"
        unit_of_measurement: "°C"
      downstairs_current_temperature:
        value_template: "{{ (state_attr('sensor.ESI_Downstairs', 'inside_temparature')|int(0))/10}}"
        friendly_name: "Downstairs Temperature"
        unit_of_measurement: "°C"
      downstairs_set_temperature:
        value_template: "{{ (state_attr('sensor.ESI_Downstairs', 'current_temprature')|int(0))/10}}"
        friendly_name: "Downstairs Set Temperature"
        unit_of_measurement: "°C"

Lastly, I am using generic thermostat devices to display the info;

climate:
  - platform: generic_thermostat
    name: Downstairs
    heater: switch.downstairs_thermostat
    target_sensor: sensor.downstairs_current_temperature
    min_temp: 15
    max_temp: 22
    ac_mode: false
    target_temp: 17
    cold_tolerance: 0.3
    hot_tolerance: 0
    min_cycle_duration:
      seconds: 5
    initial_hvac_mode: "off"
    away_temp: 16
    precision: 0.5
  - platform: generic_thermostat
    name: Upstairs
    heater: switch.upstairs_thermostat
    target_sensor: sensor.upstairs_current_temperature
    min_temp: 15
    max_temp: 22
    ac_mode: false
    target_temp: 17
    cold_tolerance: 0.3
    hot_tolerance: 0
    min_cycle_duration:
      seconds: 5
    initial_hvac_mode: "off"
    away_temp: 16
    precision: 0.5

Updating Target Temperatures
The last part is being able to update the temperature on the thermostat, this can be done using the following URL;

https://esiheating.uksouth.cloudapp.azure.com/centro/setThermostatWorkModeNew

The call needs to be of type application/x-www-form-urlencoded along with a minimum of 3 values;

device_id - Id of teh device you want to set the temperature for
user_id - The id returned from login
current_temprature - The target temperature using 3 digits e.g. 190 for 19 degrees
messageId - random message id although I have used the same id for all messages
work_mode - default is 5, I assume there are others, I havent dug much further.
token - The token value returned from the login

Again, this can be tested using Curl;

curl -X POST -d "device_id=0000000000&user_id=00000&current_temprature=195&messageId=4e07&work_mode=5&token=YourTokenValue" https://esiheating.uksouth.cloudapp.azure.com/centro/setThermostatWorkModeNew

On a successful call, values are returned but I dont do anything with these.

As mentioned in my previous post, I am not using this currently in HA as was having difficulties passing the values but have configured a rest_command and automation to test the call. In the configuration.yaml, i have the following;

rest_command:
  set_esi_temperature:
    url: https://esiheating.uksouth.cloudapp.azure.com/centro/setThermostatWorkModeNew
    method: POST
    verify_ssl: false
    payload: "device_id=000000000&user_id=00000&current_temprature=195&messageId=4e07&work_mode=5&token=YourTokenValue"
    #"device_id={{device}}&user_id={{user}}&current_temprature={{temperature}}))&messageId=32ed&work_mode=5&token={{token}}"
    content_type: 'application/x-www-form-urlencoded'

The automation to call this is as follows;

alias: Set Temperature
description: ""
mode: single
triggers:
  - entity_id: climate.upstairs
    attribute: temperature
    trigger: state
conditions: []
actions:
  - action: rest_command.set_esi_temperature
    data: {}

Its probably a simple step here to have the temperature updating properly based on the HA thermostat value.

I will post again with some details on how you could potentially get the initial login data.

1 Like

As stated in the prior posts, the main challenge is obtaining the id and token, the problem is the app encrypts/hashes the password before sending, this makes absolute sense security-wise but makes it harder to use the login outside of the app.

In order to find the encrypted password, I am using a virtual android device in Android Studio to capture the API calls using MITM proxy - https://mitmproxy.org/

There are articles available online on how you can do this and will require you to update the apk for the app to allow it to work with the proxy. There may be an additional challenge here as they have recently updated the app, currently I am still able to use the old version on my virtual device.

Once you have the encrypted value for the password you wont need to use the virtual device as it would be possible to use ha or curl to get updated token value.

I will reiterate I am not a developer but a tinkerer so I am happy to answer questions but may not have all the answers.

Whilst tinkering, I believe I have found that the password is encrypted using des/cbc/pkcs5padding, there are at least 3 passphrases contained in the apk so may be 3des and somewhere in the process it is base64 encoded. I am pretty sure the encryption is handled using the java crypto library in android but what I am missing is the key. I am not sure this is useful in any way but thought I would share it in case it is.

1 Like

Thank you so much for sharing this. It’s way beyond me, I can just about understand the yaml parts, but I’ve always felt it was best to leave the curling to the Scots, they’re very very good at it you know?

If nothing else though, you’ve made my mind up, getting this working without an official / simple unofficial integration is not going to be worth the reward. I’m going to switch this out for a Tado or similar as soon as possible.

Thanks again though, you really are a star. I’m sure anybody with more of a coding background than me will find this very useful.

Anybody want to buy an ESI series 6 wireless?

Any update on an easier way to set this up?