alex-0103 / warmup4IE

Apache License 2.0
10 stars 16 forks source link

Incompatible with latest version of HA #4

Open 8osman opened 5 years ago

8osman commented 5 years ago

Hi, just want to know if you are still working with this?

It was a great little tool when it was working and it's a shame it's not anymore. completely understand that this is something that only gets done in spare time for free.

PtolemyIV commented 5 years ago

+1 for this.. worked great previously on my system but sadly broken by latest climate changes in HA

8osman commented 4 years ago

I'm guessing Alex has been too busy to keep up with this.

I've had a go at making it work with the latest version of HA. I'm no programmer so it's probably a bit buggy.

One of the issues is setting PRESET_AWAY - it switches the unit to off, however I'm not using location sensing through warmup, so the unit then updates HA to say PRESET_HOME after a few seconds.

It doesn't switch the unit on again.

I'll probably fix this in future, but busy right now. I think it probably did this before as already in my automation it set preset away - then just auto for back on.

"""Platform that offers a connection to a warmup device."""
import logging
from typing import Optional, List

from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateDevice

from homeassistant.components.climate.const import (
    HVAC_MODE_AUTO,
    HVAC_MODE_HEAT,
    HVAC_MODE_OFF,
    PRESET_AWAY,
    PRESET_HOME,
    SUPPORT_PRESET_MODE,
    SUPPORT_TARGET_TEMPERATURE,
)

from homeassistant.const import (ATTR_TEMPERATURE, PRECISION_TENTHS, TEMP_CELSIUS, CONF_NAME, CONF_PASSWORD, CONF_USERNAME, CONF_ROOM)

from homeassistant.exceptions import PlatformNotReady

import homeassistant.helpers.config_validation as cv

from homeassistant.helpers.entity import async_generate_entity_id

from homeassistant.util.temperature import convert as convert_temperature

_LOGGER = logging.getLogger(__name__)

import voluptuous as vol

SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
SUPPORT_HVAC_HEAT = [HVAC_MODE_HEAT, HVAC_MODE_AUTO, HVAC_MODE_OFF]
SUPPORT_PRESET = [PRESET_AWAY, PRESET_HOME]

CONF_LOCATION = 'location'
CONF_TARGET_TEMP = 'target_temp'

DEFAULT_NAME = 'warmup4ie'
DEFAULT_TARGET_TEMP = 20

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Required(CONF_USERNAME): cv.string,
    vol.Required(CONF_PASSWORD): cv.string,
    vol.Required(CONF_LOCATION): cv.string,
    vol.Required(CONF_ROOM): cv.string,
    vol.Optional(CONF_TARGET_TEMP,
                 default=DEFAULT_TARGET_TEMP): vol.Coerce(float),
})

def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the Demo climate devices."""
    _LOGGER.info("Setting up platform for Warmup component")
    name = config.get(CONF_NAME)
    user = config.get(CONF_USERNAME)
    password = config.get(CONF_PASSWORD)
    location = config.get(CONF_LOCATION)
    room = config.get(CONF_ROOM)
    target_temp = config.get(CONF_TARGET_TEMP)

    from warmup4ie import Warmup4IEDevice
    device = Warmup4IEDevice(user, password, location, room,
                             target_temp)
    if device is None or not device.setup_finished:
        raise PlatformNotReady

    add_entities(
            [Warmup(hass, name, device)])

class Warmup(ClimateDevice):
    """Representation of a Warmup device."""

    mode_map = {'prog':HVAC_MODE_AUTO, 'fixed':HVAC_MODE_HEAT, 'off':HVAC_MODE_OFF}

    def __init__(self, hass, name, device):
        """Initialize the climate device."""
        _LOGGER.info("Setting up Warmup component")
        self._name = name
        self._hvac_mode = HVAC_MODE_AUTO
        self._hvac_list = [HVAC_MODE_HEAT, HVAC_MODE_OFF, HVAC_MODE_AUTO]
        self._unit_of_measurement = TEMP_CELSIUS
        self._on = True
        self._away_mode = False
        self._device = device
        self._support_flags = SUPPORT_FLAGS | SUPPORT_PRESET_MODE
        self._awaymodeLastState = HVAC_MODE_OFF

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

    @property
    def hvac_mode(self):
        """Return current HVAC mode."""
        return self._hvac_mode

    @property
    def hvac_modes(self):
        """Return available HVAC modes."""
        return self._hvac_list

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

    @property
    def name(self):
        """Return the name of the climate device."""
        return self._name

    @property
    def temperature_unit(self):
        """Return the unit of measurement."""
        return self._unit_of_measurement

    @property
    def current_temperature(self):
        """Return the current temperature."""
        return self._device.get_current_temmperature()

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._device.get_target_temmperature()

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        return self._device.get_target_temperature_low()

    @property
    def max_temp(self):
        """Return the maximum temperature."""
        return self._device.get_target_temperature_high()

    @property
    def preset_mode(self):
        """Return the current preset mode, e.g., home, away, temp."""
        if self._away_mode:
            return PRESET_AWAY
        return PRESET_HOME

    @property
    def preset_modes(self):
        """Return a list of available preset modes."""
        return [PRESET_HOME, PRESET_AWAY]

    @property
    def is_away_mode_on(self):
        """Return if away mode is on."""
        return self._away_mode

    @property
    def is_on(self):
        """Return true if the device is on."""
        return self._on

    @property
    def current_hvac(self):
        """Return current operation ie. heat, auto, off."""
        return self._current_hvac_mode

    def set_temperature(self, **kwargs):
        """Set new target temperatures."""
        if kwargs.get(ATTR_TEMPERATURE) is not None:
            self._device.set_new_temperature(kwargs.get(ATTR_TEMPERATURE))

    def turn_away_mode_on(self):
        """Turn away mode on."""
        self._away_mode = True

    def turn_away_mode_off(self):
        """Turn away mode off."""
        self._away_mode = False

    def set_hvac_mode(self, hvac_mode):
        """Set hvac mode."""
        if hvac_mode == HVAC_MODE_HEAT:
            self._hvac_mode = HVAC_MODE_HEAT
            self._device.set_temperature_to_manual()
        elif hvac_mode == HVAC_MODE_AUTO:
            self._hvac_mode = HVAC_MODE_AUTO
            self._device.set_temperature_to_auto()
        elif hvac_mode == HVAC_MODE_OFF:
            self._hvac_mode = HVAC_MODE_OFF
            self._device.set_location_to_off()
        else:
            _LOGGER.error("Unrecognized hvac mode: %s", hvac_mode)
            return
        # Ensure we update the current operation after changing the mode
        self.schedule_update_ha_state()

    def set_preset_mode(self, preset_mode):
        if self._on == False: 
            return
        if preset_mode == PRESET_AWAY:
            if self._away_mode == False:
                self._awaymodeLastState = self._hvac_mode
                self._away_mode = True
                self.set_hvac_mode(HVAC_MODE_OFF)
        elif preset_mode == PRESET_HOME:
            if self._away_mode == True:
                self._away_mode = False
                self.set_hvac_mode(self._awaymodeLastState)
        else:
            _LOGGER.error("Unknown mode: %s", preset_mode)
        self.schedule_update_ha_state()

    def turn_on(self):
        """Turn on."""
        self._on = True
        self._device.set_temperature_to_manual()

    def turn_off(self):
        """Turn off."""
        self._on = False
        self._device.set_location_to_off()

    def update(self):
        """Fetch new state data for this device.
        This is the only method that should fetch new data for Home Assistant.
        """
        if not self._device.update_room():
            _LOGGER.error("Updating Warmup component failed")

        # set operation mode
        self._current_hvac_mode = self.mode_map.get(
            self._device.get_run_mode())

        # set whether device is in away mode
        if self._device.get_run_mode() == 'away':
            self._away_mode = True
        else:
            self._away_mode = False

        # set whether device is on/off
        if self._device.get_run_mode() == 'off':
            self._on = False
        else:
            self._on = True
        `
8osman commented 4 years ago

Also noted on the pypi release Alex left it at version 0.1.5

https://pypi.org/project/warmup4ie/

PtolemyIV commented 4 years ago

Hi 8osman - funnily enough I did exactly the same thing to make it work for myself - I have not programmed since my old C++ days so half did this for learning python etc too.

My attempt can be found at: https://github.com/PtolemyIV/warmup4ie/blob/master/climate.py

If anyone tests it more extensively and finds obvious issues etc, please let me know since this was hacked together in a few hours . Alex - I am obviously not a contributor for your repository so please feel free to take this and incorporate as you see fit and I will delete from my account to avoid risk of confusion.

The one issue that I found is that, at least on the official WarmUp app, the location modes apply across all rooms - I have 3 different warmup rooms at home and obviously each climate lovelace card (I am using simple-thermostat) has its own "Heat, Auto, Off" buttons - if you click Off on one room, then all the rooms get turned off. However, if you then click Auto, only that individual room gets turned on again. So its a little odd/inconsistent.

8osman commented 4 years ago

haha, well done, better job than me! 👍

PtolemyIV commented 4 years ago

Please test it first..! Seems to work okay for me aside from location vs room issue I mention above... I'd also more generally like to have the single component call set up climate devices for all the rooms rather than needing to set up each individually (and could more readily get any device being switched back on, switch back on all the other devices)

The feature I really want on all my climate devices is a "temporary boost" button - i.e. turn on the heating for an hour in a specific room - i know you can do with scripts but both warmup and tado support these timed overrides.

foxy82 commented 4 years ago

I'm testing this now. First problem is that if the mode is override you get a key error but I think that is related to the warmup api - another PR i've done to expose more data should fix that.

https://github.com/alex-0103/warmup4IE/pull/6

I do feel the fact that if you have more than one device this is horribly inefficient as it polls the API and downloads every devices data each time you call update_room. So I have 5 devices so I make 5 api calls but on each one I get all the data so I could just make one. I'm going to see if I can fix that.

rct commented 4 years ago

@foxy82 - Thank you for your work on this. I've got 4 warmup4ie's installed, so I too would be very interested in some restructuring that would make things a bit more efficient.

rct commented 4 years ago

This might belong in a separate issue.

The current API uses one object to represent the connection and the current device. (The current device appears to be a room in warmup's terms, ) The queries used currently operate on all of the devices (rooms) and the data for the room other than the selected one is thrown away. (I don't know if multiple locations existed under one account if the API calls to warmup would return all locations and all devices at once.)

I think it might make sense to have an object that represents the connection to warmup and then a device object for each room would make more sense.

The connection object should probably keep track of when the data was last updated.

For use outside of home assistant, I'd prefer a model where the connection would return a list of locations/devices, or list of objects for each device, so no hard coded configuration for location names and room names would be needed. It's possible this model would also work better with home assistant auto discovery, but I don't know much about that yet. Currently __init__ calls self.get_all_devices which essentially does this.

Also, currently __init__ doesn't require a location or room name. When an object is instantiated without that, it seems to default to the first device. I don't know if that is supposed to work that way or not. If called without location/room names it could return a list of devices or objects.

The question is whether the break the currently exposed API at this point or not.

Thoughts?

foxy82 commented 4 years ago

@PtolemyIV I've taken your work and exteded to reduce the number of API calls as well as some other good things. All in this PR: https://github.com/alex-0103/warmup4IE/pull/8

If you can try it out and let me know how you get on that would be great. @rct also any feedback would be appreciated.

PtolemyIV commented 4 years ago

Excellent will try it when back (in 10 days only tho)

alex-0103 commented 4 years ago

hi folks, just wanted to give a livesign. Indeed i was very busy the last months and was even unable to check the issues that have been raised in the last months. After having upgraded my own HA installation to the latest version I can confirm that my original code was broken due to changes in HA that were introduced in Versions 0.9x+. I have checked the updated climate.py that has been submitted by @PtolemyIV at https://github.com/PtolemyIV/warmup4ie and confirm that this works for me. Very good work! Thanks! Since i only work occasionally on this project, maybe someone is interested to take over this component and make it an official part HA? I attempted this in summer 2019 but then HA introduced breaking changes and I had no time to rewrite the code. regards Alex

foxy82 commented 4 years ago

Thanks for all your work so far. PR #8 goes a little further and I've made it so that if you have multiple thermostats it updates them in a single call to My Warmup. Also it will auto detect and add all thermostats to HA that are in a warm-up account. However all of that will require a new version of the API being pushed to pypi. How do you feel about that? If you are happy I will try to get it integrated into HA.

PtolemyIV commented 4 years ago

Thanks for all your work so far. PR #8 goes a little further and I've made it so that if you have multiple thermostats it updates them in a single call to My Warmup. Also it will auto detect and add all thermostats to HA that are in a warm-up account. However all of that will require a new version of the API being pushed to pypi. How do you feel about that? If you are happy I will try to get it integrated into HA.

I agree this makes sense and thanks both

alex-0103 commented 4 years ago

Thanks for all your work so far. PR #8 goes a little further and I've made it so that if you have multiple thermostats it updates them in a single call to My Warmup. Also it will auto detect and add all thermostats to HA that are in a warm-up account. However all of that will require a new version of the API being pushed to pypi. How do you feel about that? If you are happy I will try to get it integrated into HA. What changes would be necessary? Please feel free to enhance the code as you see fit. regards Alex

foxy82 commented 4 years ago

They are all in PR #8 I made the API so you have a class representing the connection and another one for the devices. The connection then only requires a username/password and will return a device for everything it finds. Calling update will then update will devices. I've also fixed up warmup_cc to work with this.

8osman commented 4 years ago

@foxy82

@PtolemyIV I've taken your work and exteded to reduce the number of API calls as well as some other good things. All in this PR:

8

If you can try it out and let me know how you get on that would be great. @rct also any feedback would be appreciated.

I've given this a try, it looks like it doesn't need as much setup in configuration.yaml as previous no name no location no room

I had to change the domain in climate.py to warmup_cc from warmup otherwise it was getting lost...

Also in the warmup4ie folder under main.py are maybe a couple of details that need addressing?

You've put a load of good work into this, which is awesome. Some things are working better, some maybe need a bit more tweaking.

Boost is showing as an option within climate display, but fails to make call..... I would guess these parameters need to be set?

Looks like great progress 👍

foxy82 commented 4 years ago

I've given this a try, it looks like it doesn't need as much setup in configuration.yaml as previous no name no location no room

Correct it will load all thermostats it finds under the warmup account and will use the name they have on the account. I only have one location on my account so haven't tested multiple locations but it should work.

I had to change the domain in climate.py to warmup_cc from warmup otherwise it was getting lost...

Yeah I've made this as if it will be a full component rather than a custom component (which.is what I assume the cc stands for). If you change configuration.yaml to be warmup instead of warmup_cc it should work without modifying the domain.

Also in the warmup4ie folder under main.py are maybe a couple of details that need addressing?

You've put a load of good work into this, which is awesome. Some things are working better, some maybe need a bit more tweaking.

Sure what do you think needs changing?

Boost is showing as an option within climate display, but fails to make call..... I would guess these parameters need to be set?

Interesting I added boost but then thought I removed it for that reason. To confirm how are you "boosting" in the UI? Boosting is interesting for the warmup I'd imagine that is an override for an amount of time? If so what would we suggest? 1 hour of additional heat? Would we need a boost temp or just X degrees above the current setting? I have created a service that allows you to do an override specifying the temperature and length of the boost perhaps with that we can disable a direct boost from the UI?

Screenshot_20200103-083125

Looks like great progress

Thanks and thanks for testing/feeding back. Happy new year.

artmg commented 4 years ago

Hi @foxy82 @8osman @PtolemyIV - it's great to see others all keen on moving this work on in alex's absence. When I checked before xmas foxy's fork had the most recent work so I worked on a PR https://github.com/foxy82/warmup4IE/pull/1 for that. However I now wonder if Ptolemy's is not the most recent?

What I have done is created a new owner https://github.com/ha-warmup and as soon as we choose between our forks whichever is the most up to date I will fork that in and make you contributors so that we can work together.

If you want to compare my fork please see https://github.com/artmg/warmup4IE/tree/ft_init_loader

Looking forward to driving this integration forward together. Cheers, Art

PtolemyIV commented 4 years ago

Thanks - foxy82's is the latest. I'll remove my github shortly to avoid confusion. And will also get around to testing his as well.

On Sat, 4 Jan 2020, 15:22 Art M. Gallagher, notifications@github.com wrote:

Hi @foxy82 https://github.com/foxy82 @8osman https://github.com/8osman @PtolemyIV https://github.com/PtolemyIV - it's great to see others all keen on moving this work on in alex's absence. When I checked before xmas foxy's fork had the most recent work so I worked on a PR foxy82#1 https://github.com/foxy82/warmup4IE/pull/1 for that. However I now wonder if Ptolemy's is not the most recent?

What I have done is created a new owner https://github.com/ha-warmup https://github.com/ha-warmup and as soon as we choose between our forks whichever is the most up to date I will fork that in and make you contributors so that we can work together.

  • Please vote whichever of your forks you think is the furthest ahead so I can fork it and we can start
  • Please also mention if you want to start a channel for dev discussions like reddit or discus.

If you want to compare my fork please see https://github.com/artmg/warmup4IE/tree/ft_init_loader

Looking forward to driving this integration forward together. Cheers, Art

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/alex-0103/warmup4IE/issues/4?email_source=notifications&email_token=AAEEYZ7I5NFJGCGGMGJRHZDQ4CSRNA5CNFSM4I4V6CMKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEICZ6GA#issuecomment-570793752, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAEEYZ6N4XYZSXOWAQWKFILQ4CSRNANCNFSM4I4V6CMA .

artmg commented 4 years ago

so we now have https://github.com/ha-warmup/warmup4IE and I have made us all contributors. Let's use the Issues for managing important things that need resolving with the codebase. Hopefully putting our heads together will make it easier for us all.

artmg commented 4 years ago

@alex-0103

hi folks, just wanted to give a livesign. Indeed i was very busy the last months and was even unable to check the issues that have been raised in the last months. ... Since i only work occasionally on this project, maybe someone is interested to take over this component and make it an official part HA?

I just raised you PR #9 so visitors can see what doesn't work and where to go to get a newer version of this component. Many thanks for giving us such a great base to work from!