New climate / thermostat implementation tips

If it’s a thermostat you need to inherit from the ClimateDevice doing so would make a climate card with the correct settings display in the frontend.

1 Like

Thanks for your help! Any other suggestions? It sounds like a class in PHP which I can implement but no idea how that works in Python. When I inherit it I can implement some functions and do the http requests there to get it working? What about a loop so the sensors will be updated every minute?

Correct, so if you look at https://github.com/home-assistant/home-assistant/blob/b6d3a199ce25740f72cac686f7efa8be17aa7a95/homeassistant/components/climate/generic_thermostat.py#L93 it inherits from ClimateDevice on that line.

If you override the method should_poll and return true the default polling interval is 60 seconds. So every 60 seconds the method def update(self): will be called for your platform. Inside that method you would need to make your network call, (call to your library which makes a call to the API)

1 Like

Thanks again! I’m going to experiment with it. Your last line, is a library needed? Because there isn’t one I think. It’s possible to do the http request in that update method directly right? Let’s see how it goes, to be continued!

Edit; found one: https://github.com/bwesterb/incomfort

It is recommend that a library be used. Not sure it will get approved without using/making one to use. We don’t make to limit the use of the code to HA and should make/use a generic library that other people can use for other things.

I’ve used this as reference: https://github.com/daveNewcastle/HassIO-config/commit/99d6d704bfede904cddf6bef1e1d26fa68f8ad6a#diff-b93b5f54ec74c57c171d6b2efb485dab and got it working! I’ve created a ~/.homeassistant/custom_components/climate/InComfort.py file with:

import json
import logging

import voluptuous as vol
import homeassistant.helpers.config_validation as cv

from urllib.request import urlopen
from homeassistant.components.climate import (
    STATE_HEAT, ClimateDevice, SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA)
from homeassistant.const import (
    TEMP_CELSIUS, ATTR_TEMPERATURE)

CONF_HOST = 'host'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
})

def _lsbmsb(lsb, msb):
    return (lsb + msb*256) / 100.0

def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the InComfort thermostat."""

    host = config.get(CONF_HOST)

    add_devices([InComfortThermostat(host)])

class InComfortThermostat(ClimateDevice):
    """Representation of a InComfort Thermostat device."""

    def __init__(self, host):
        """Initialize the thermostat."""
        self.host = host
        self._name = "InComfort"
        self._current_temperature = None
        self._target_temperature = None
        self.data = None

    @property
    def supported_features(self):
        """Return the list of supported features."""
        return SUPPORT_TARGET_TEMPERATURE

    @property
    def name(self):
        """Return the name of the thermostat, if any."""
        return self._name

    @property
    def temperature_unit(self):
        """Return the unit of measurement which this thermostat uses."""
        return TEMP_CELSIUS

    @property
    def current_temperature(self):
        """Return the current temperature."""
        if self.data is not None:
            self._current_temperature = _lsbmsb(self.data['room_temp_1_lsb'], self.data['room_temp_1_msb'])
        else:
            self._current_temperature = None
        return self._current_temperature

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        if self.data is not None:
            self._target_temperature = _lsbmsb(self.data['room_temp_set_1_lsb'], self.data['room_temp_set_1_msb'])
        else:
            self._target_temperature = None
        return self._target_temperature

    @property
    def should_poll(self):
        """Return the polling state."""
        return True

    def set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        if temperature is None:
            return

        urlopen('http://' + self.host + '/data.json?heater=0&thermostat=0&setpoint=' + str((min(max(temperature, 5), 30) - 5.0) * 10))
        self._target_temperature = temperature

    def update(self):
        """Get the latest data."""
        response = urlopen('http://' + self.host + '/data.json?heater=0')
        string = response.read().decode('utf-8')
        self.data = json.loads(string)

And added this in my ~/.homeassistant/configuration.yaml file:

climate:
  # InComfort
  - platform: InComfort
    host: 192.168.1.139

But…

  1. I did not manage to get it working with the library (https://github.com/bwesterb/incomfort). Is it just a CLI tool?
  2. The current setpoint is called “Unknown”, see:

    How can I change that?
  3. When Home Assistant starts it just says “Unknown” until 1 minute passes and the update is triggered, see:

    How can I make sure the update is triggered on load?
  4. When I change the temperature it’s not directly reflected while I’m setting it after changing with self._target_temperature = temperature

Any tips? Or other suggestions to improve this?

The library seems really old, last update was 3 years back. Probably would be best to start a new one, but you could use that one as a starting place for how to create yours.

The way most of the components work in HA is during setup they call out to whatever library it is using passing it username and password or whatever, and then it stores all of the device information inside a library object. Then home assistant would ask that library for the store device information in the libraries object (in memory) the only time an API call (I/O operation) should be run is during the update method. Since this happens during the creation of the platform once it is added to HA it will have up-to-date info (you wont have to wait 60 for the first update)

What all features are supported by the InComfort device? You will need to make sure supported_features is returning the correct information it should be the sum of all supported features which is located here https://github.com/home-assistant/home-assistant/blob/355005114b3315694e3a9654249d4a1998d76fcd/homeassistant/components/climate/init.py#L52

Anything showing as Unknown would do so because you are returning None from, for example current_temperature. I assume that means self.data is None.

To force an update immediately after you made a change so you don’t have to wait 60 seconds for the next poll you can call self.schedule_update_ha_state(True) which should force a call to update.

Is this room_temp_set_1_lsb unique to your setup? Like a name? If so, can it not be pulled from some generic endpoint? If not, it would probably be best to make that a config entry option so other users can pass it in via their config.

So I have made several library for use with HA. Here is a simple library I made https://github.com/w1ll1am23/pytrackr that is pretty basic. That library is then push to pypi.org so Home Assistant can pull it automatically.

@w1ll1am23 thanks again for your help!

Creating a library is a little bit too much for me with my limited Python knowledge. So I keep it this way for now, drawback of that is that it probably won’t get accepted as PR into the core. I’ll put it on Github so maybe someone else with more knowledge of Python and Home Assistant can do that. See: https://github.com/royduin/home-assistant-incomfort

I’ve checked the features but the InComfort thermostat is very basic. Just a current and target temperature, that’s it. In that json is a lot more information but that’s not very relevant to display in Home Assistant I think.

I’ve fixed the “Unknown”, just had to set a state. But problem 3 is still there. When I (re)start Home Assistant I’ve to wait for 1 minute to get data. Is it possible to trigger the update when starting so I don’t have to wait 1 minute? Putting self.schedule_update_ha_state(True) in the init method doesn’t work.

And no room_temp_set_1_lsb is not unique, it’s the same for every InComfort gateway.

You should be able to call self.update() in the init method.

Awesome! It’s working, thanks @w1ll1am23, mentioned you in the commit; https://github.com/royduin/home-assistant-incomfort/commits/master :cowboy_hat_face:

Hi,
i have the same issue in a modbus climate controls. I fixed the original code due it stopped to works and it returns uncorrect value. but I have the unknown on the setpoint description

How do you fix the code?

Thanks

You’ve to set the state, see: https://github.com/royduin/home-assistant-incomfort/blob/master/InComfort.py#L46

Nice work @royduin!

1 Like

@royduin, looks like my setup is ‘offline’ every 4 hours :frowning:

For example, this is @ 12:17:
image

This is 4 hours later @ 16:17:
image

The same for every 4 hours before…

Are you having a similar behaviour?

I want to create a graph on temp and set temp but because of this I am getting an unexpected result…

image

That’s weird, I’m not having that issue. Is it really a InComfort issue or maybe a networking issue? Anything in your logs?

Nothing in the logs. I have a newer version of the gateway. In the documentation it is mentioning automatic restarts to ensure the connection doesn’t drop :roll_eyes: maybe this is the problem… Which version of the gateway do you have?

Latest update to 0.89.1 gives the following error

2019-03-10 22:21:35 ERROR (MainThread) [homeassistant.loader] Error loading custom_components.InComfort.climate. Make sure all dependencies are installed Traceback (most recent call last): File “/usr/local/lib/python3.7/site-packages/homeassistant/loader.py”, line 166, in _load_file module = importlib.import_module(path) File “/usr/local/lib/python3.7/importlib/init.py”, line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File “<frozen importlib._bootstrap>”, line 1006, in _gcd_import File “<frozen importlib._bootstrap>”, line 983, in _find_and_load File “<frozen importlib._bootstrap>”, line 967, in _find_and_load_unlocked File “<frozen importlib._bootstrap>”, line 677, in _load_unlocked File “<frozen importlib._bootstrap_external>”, line 728, in exec_module File “<frozen importlib._bootstrap>”, line 219, in _call_with_frames_removed File “/config/custom_components/InComfort/climate.py”, line 8, in <module> from homeassistant.components.climate import ( ImportError: cannot import name ‘STATE_HEAT’ from ‘homeassistant.components.climate’ (/usr/local/lib/python3.7/site-packages/homeassistant/components/climate/init.py) 2019-03-10 22:21:35 ERROR (MainThread) [homeassistant.loader] Unable to find platform InComfort.

Change line 8 from:

from homeassistant.components.climate import (
STATE_HEAT, ClimateDevice, SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA)

to

from homeassistant.components.climate import (ClimateDevice, PLATFORM_SCHEMA)
from homeassistant.components.climate.const import (STATE_HEAT, SUPPORT_TARGET_TEMPERATURE)

Thanks prier…works fine again after that fix :wink: