Hive Heating UK

Hi, has anyone tried to get Hive Heating built into HA? I currently have version 1 and would love to get this included until I get a new system at a later date.

Thanks

1 Like

Hi Mike. Did you get you Hive working with HA? I am looking to do the same thing.

Chris

And me all so I have the British Gas Hive.
I did a Fing scan and it has SSH port 22.

Hi just found a few URLs.

http://www.smartofthehome.com/2016/05/hive-rest-api-v6/

Hope this helps some to get hive working with Home Assistant.

1 Like

Hi guys,

I’m considering getting a Hive as I think the Nest is not compatible with my boiler. Just wondering has anyone managed to get theirs into HA?

Got it working with the API they use for the app and web (undocumented APIs are the worst). :cat2:
Not sure if there’s a rate limit on the API though, haven’t tested it in HA long enough.

You can download the Python file here and give it a test my changing nodes/6c78fac7-9997-4043-aa24-577595ff6677 to the ID or YOUR thermostat, you can find this by looking through the API, http://www.smartofthehome.com/2016/05/hive-rest-api-v6/ should help you figure some of it out. I know it’s not the best at the moment. :-S

Just drop it into custom_components/climate and add:

- platform: hiveheat
  username: [email]
  password: [password]

to your config. :smiley_cat:

3 Likes

Hi GreenTurtwig

Thanks for looking at this i have downloaded your .py file have some problems with it…

16-11-10 17:36:13 homeassistant.components.climate: Error while setting up platform hiveheat
Traceback (most recent call last):
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py”, line 148, in _async_setup_platform
entity_platform.add_entities, discovery_info
File “/usr/lib/python3.4/asyncio/futures.py”, line 388, in iter
yield self # This tells Task to wait for completion.
File “/usr/lib/python3.4/asyncio/tasks.py”, line 286, in _wakeup
value = future.result()
File “/usr/lib/python3.4/asyncio/futures.py”, line 277, in result
raise self._exception
File “/usr/lib/python3.4/concurrent/futures/thread.py”, line 54, in run
result = self.fn(*self.args, **self.kwargs)
File “/home/hass/.homeassistant/custom_components/climate/hiveheat.py”, line 36, in setup_platform
add_devices([HiveThermostat(requests, url, username, password)])
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py”, line 289, in add_entities
).result()
File “/usr/lib/python3.4/concurrent/futures/_base.py”, line 402, in result
return self.__get_result()
File “/usr/lib/python3.4/concurrent/futures/_base.py”, line 354, in __get_result
raise self._exception
File “/usr/lib/python3.4/asyncio/tasks.py”, line 233, in _step
result = coro.throw(exc)
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py”, line 304, in async_add_entities
yield from asyncio.gather(*tasks, loop=self.component.hass.loop)
File “/usr/lib/python3.4/asyncio/futures.py”, line 388, in iter
yield self # This tells Task to wait for completion.
File “/usr/lib/python3.4/asyncio/tasks.py”, line 286, in _wakeup
value = future.result()
File “/usr/lib/python3.4/asyncio/futures.py”, line 277, in result
raise self._exception
File “/usr/lib/python3.4/asyncio/tasks.py”, line 237, in _step
result = next(coro)
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py”, line 320, in _async_process_entity
new_entity, self, update_before_add=update_before_add
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity_component.py”, line 194, in async_add_entity
yield from entity.async_update_ha_state()
File “/srv/hass/lib/python3.4/site-packages/homeassistant/helpers/entity.py”, line 216, in async_update_ha_state
state = self.state
File “/srv/hass/lib/python3.4/site-packages/homeassistant/components/climate/init.py”, line 369, in state
if self.current_operation:
File “/home/hass/.homeassistant/custom_components/climate/hiveheat.py”, line 103, in current_operation
if self._thermostatParsed[“activeScheduleLock”][“targetValue”] == False and self._thermostatParsed[“activeHeatCoolMode”][“reportedValue”] == “HEAT”:
KeyError: ‘activeScheduleLock’

Thanks for your input on this.

I believe that error is because the dictionary (self.thermostatParsed) doesn’t contain the value activeScheduleLock. Are you sure the ID is of a thermostat and not something else? You can use Postman to find the correct ID. :cat2:

Hi

Yes managed to use postman and got the following info.

  "id": "3cda33e2-7c2b-4d96-977e-xxxxxxxxxx",
  "href": "https://api.prod.bgchprod.info:8443/omnia/nodes/3cda33e2-7c2b-4d96-977e-xxxxxxxxxx",
  "links": {},
  "name": "Receiver",
  "nodeType": "http://alertme.com/schema/json/node.class.thermostat.json#",
  "parentNodeId": "7dcb0b45-37a3-49ba-b5dc-xxxxxxxxxx",
  "lastSeen": 1478797554541,
  "createdOn": 1473424090101,
  "userId": "f3ae703e-262b-4317-8f65-xxxxxxxxxx",
  "ownerId": "f3ae703e-262b-4317-8f65-xxxxxxxxxx",

I have but in the xxxxxxxxxx in for security. i have tried using parentNodeId and the first ID and got the same results.

Do you happen to have Hive hot water too? The API returns three devices named “Receiver” with the thermostat nodeType for me. Have a look and see if it’s returning a second or even third “thermostat” and try that, for me only the first and third ones had a activeScheduleLock keyword under attributes. :cat:

First:

Second:

Third:

The first is the proper thermostat, the third one looks like it has something to do with the hot water and I have no idea what the second one is for.

Hi

Got a reply from the postman “id” got from it.

Mine looks a bit different from yours

i still get the same responce

File “/srv/hass/lib/python3.4/site-packages/homeassistant/components/climate/init.py”, line 369, in state
if self.current_operation:
File “/home/hass/.homeassistant/custom_components/climate/hiveheat.py”, line 103, in current_operation
if self._thermostatParsed[“activeScheduleLock”][“targetValue”] == False and self._thermostatParsed[“activeHeatCoolMode”][“reportedValue”] == “HEAT”:
KeyError: ‘targetValue’

That’s odd, it should have the target value too. :-/
The “joy” of undocumented APIs…
I’ll see what I can do tomorrow, might have to take a look at how the site works again to figure out how they’re determining the state.

Taking a look at your screenshot now, it definately seems like that’s the hot water since no temperature value is being reported. :crying_cat_face:
There must be the actual thermostat there?

Hi

I enter this https://api-prod.bgchprod.info:443/omnia/nodes into the postman and get this back.

Hive_2.yaml (72.7 KB)

Hope this helps i have tried the 2230425f-6ce9-4c35-bbd0-6ac45176955b and it gives back the same problem…

Hi,

I also have the same error. See below.

Traceback (most recent call last):
File “/usr/local/lib/python3.4/dist-packages/homeassistant/helpers/entity_component.py”, line 151, in _async_setup_platform
entity_platform.add_entities, discovery_info
File “/usr/lib/python3.4/asyncio/futures.py”, line 388, in iter
yield self # This tells Task to wait for completion.
File “/usr/lib/python3.4/asyncio/tasks.py”, line 286, in _wakeup
value = future.result()
File “/usr/lib/python3.4/asyncio/futures.py”, line 277, in result
raise self._exception
File “/usr/lib/python3.4/concurrent/futures/thread.py”, line 54, in run
result = self.fn(*self.args, **self.kwargs)
File “/home/pi/.homeassistant/custom_components/climate/hiveheat.py”, line 36, in setup_platform
add_devices([HiveThermostat(requests, url, username, password)])
File “/home/pi/.homeassistant/custom_components/climate/hiveheat.py”, line 53, in init
self.update()
File “/home/pi/.homeassistant/custom_components/climate/hiveheat.py”, line 135, in update
token = loginParsed[“sessions”][0][“sessionId”]
KeyError: ‘sessions’

Yeah, this is sort of a pain to sort out with Hive’s API. Can’t really figure stuff out easily unless I have access to peoples devices. :crying_cat_face:
When/if Hive gets an official API (they said they will have one eventually) then it will be easier to sort out having a Home Assistant component for them. But until then it’s a big pain using their API.
:cat2:

Hi,

My error seems slightly different and related to the session ID -

File “/home/pi/.homeassistant/custom_components/climate/hiveheat.py”, line 135, in update
token = loginParsed[“sessions”][0][“sessionId”]
KeyError: ‘sessions’

I’m trying to figure out why.

1 Like

I solved the last the last error - I had made a typo copying the program file across. I think I’m getting close - I now have a new error that seems related to the temperature state.

6-11-24 19:41:51 ERROR (MainThread) [homeassistant.core] Error doing job: Task exception was never retrieved
Traceback (most recent call last):
File “/usr/lib/python3.4/asyncio/tasks.py”, line 237, in _step
result = next(coro)
File “/usr/local/lib/python3.4/dist-packages/homeassistant/helpers/entity_component.py”, line 335, in _async_process_entity
new_entity, self, update_before_add=update_before_add
File “/usr/local/lib/python3.4/dist-packages/homeassistant/helpers/entity_component.py”, line 204, in async_add_entity
yield from entity.async_update_ha_state()
File “/usr/local/lib/python3.4/dist-packages/homeassistant/helpers/entity.py”, line 224, in async_update_ha_state
attr = self.state_attributes or {}
File “/usr/local/lib/python3.4/dist-packages/homeassistant/components/climate/init.py”, line 379, in state_attributes
self._convert_for_display(self.current_temperature),
File “/usr/local/lib/python3.4/dist-packages/homeassistant/components/climate/init.py”, line 568, in _convert_for_display
value = convert_temperature(temp, self.temperature_unit,
File “/usr/local/lib/python3.4/dist-packages/homeassistant/components/climate/init.py”, line 435, in temperature_unit
raise NotImplementedError
NotImplementedError

Thanks for all the work/help on this guys I got this working, it would be nice to get the hot water added too but thats out of my depth :frowning: I got to the same error as you ckendall, you just need to add this to your hiveheat.py script:

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

Here is my full script if it helps anyone out, just replace *** YOUR HIVE ADDRESS *** with your code:

"""
Support for Hive thermostats.

"""
import logging, json
import voluptuous as vol

from homeassistant.components.climate import ClimateDevice
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import TEMP_CELSIUS, CONF_USERNAME, CONF_PASSWORD, ATTR_TEMPERATURE, STATE_OFF, STATE_UNKNOWN, STATE_ON
import homeassistant.helpers.config_validation as cv

STATE_SCHEDULE = "schedule"
STATE_MANUAL = "manual"

REQUIREMENTS = ['requests==2.11.1']

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

def setup_platform(hass, config, add_devices, discovery_info=None):
    """Setup Hive thermostats."""
    import requests

    url = "https://api-prod.bgchprod.info/omnia/"

    username = config[CONF_USERNAME]
    password = config[CONF_PASSWORD]

    if username is None or password is None:
        _LOGGER.error("Missing required configuration items %s or %s",
                      CONF_USERNAME, CONF_PASSWORD)
        return False

    add_devices([HiveThermostat(requests, url, username, password)])

    return


class HiveThermostat(ClimateDevice):
    """Representation of a Nest thermostat."""

    def __init__(self, requests, url, username, password):
        """Initialize the thermostat."""
        self._requests = requests
        self._url = url
        self._username = username
        self._password = password
        self._thermostatParsed = None
        self._operation_list = [STATE_SCHEDULE, STATE_MANUAL, STATE_OFF]
        self._headers = None
        self.update()

    @property
    def name(self):
        """Return the name of the thermostat, if any."""
        return "Hive Thermostat"
		
    @property
    def temperature_unit(self):
        """Return the unit of measurement."""
        return TEMP_CELSIUS
		
    @property
    def unit_of_measurement(self):
        """Return the unit of measurement which this thermostat uses."""
        return TEMP_CELSIUS

    @property
    def current_temperature(self):
        """Return the current temperature."""
        actualTemp = self._thermostatParsed["temperature"]["reportedValue"]
        return actualTemp

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        targetTemp = self._thermostatParsed["targetHeatTemperature"]["reportedValue"]
        if targetTemp == 1:
            targetTemp = self._thermostatParsed["frostProtectTemperature"]["reportedValue"]
        return targetTemp

    def set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        if temperature is None:
            return
        payload = '{"nodes":[{"attributes":{"targetHeatTemperature":{"targetValue":' + str(temperature) + '}}}]}'
        self._requests.put(self._url + "nodes/*** YOUR HIVE ADDRESS ***", data=payload, headers=self._headers)
        self._target_temperature = temperature

    @property
    def min_temp(self):
        """Identify minHeatTemperature in Hive API."""
        minTemp = self._thermostatParsed["minHeatTemperature"]["reportedValue"]
        return minTemp

    @property
    def max_temp(self):
        """Identify maxHeatTemperature in Hive API."""
        maxTemp = self._thermostatParsed["maxHeatTemperature"]["reportedValue"]
        return maxTemp

    @property
    def current_operation(self):
        """Return current operation ie. schedule, off, manual."""
        if self._thermostatParsed["activeScheduleLock"]["targetValue"] == False and self._thermostatParsed["activeHeatCoolMode"]["reportedValue"] == "HEAT":
            return STATE_SCHEDULE
        elif self._thermostatParsed["activeHeatCoolMode"]["reportedValue"] == "OFF":
            return STATE_OFF
        elif self._thermostatParsed["activeScheduleLock"]["targetValue"] == True and self._thermostatParsed["activeHeatCoolMode"]["reportedValue"] == "HEAT":
            return STATE_MANUAL
        else:
            return STATE_UNKNOWN

    def set_operation_mode(self, operation_mode):
        """Set operation mode."""
        if operation_mode == STATE_SCHEDULE:
            payload = '{"nodes":[{"attributes":{"activeHeatCoolMode":{"targetValue":"HEAT"},"activeScheduleLock":{"targetValue":false}}}]}'
            self._requests.put(self._url + "nodes/*** YOUR HIVE ADDRESS ***", data=payload, headers=self._headers)
        elif operation_mode == STATE_OFF:
            payload = '{"nodes":[{"attributes":{"activeHeatCoolMode":{"targetValue":"OFF"},"activeScheduleLock":{"targetValue":true}}}]}'
            self._requests.put(self._url + "nodes/*** YOUR HIVE ADDRESS ***", data=payload, headers=self._headers)
        elif operation_mode == STATE_MANUAL:
            payload = '{"nodes":[{"attributes":{"activeHeatCoolMode":{"targetValue":"HEAT"},"activeScheduleLock":{"targetValue":true}}}]}'
            self._requests.put(self._url + "nodes/*** YOUR HIVE ADDRESS ***", data=payload, headers=self._headers)

    @property
    def operation_list(self):
        """List of available operation modes."""
        return self._operation_list

    def update(self):
        """Get the latest data."""
        payload = '{"sessions":[{"username":"' + self._username + '","password":"' + self._password + '","caller":"WEB"}]}'
        headers = {'content-type':"application/vnd.alertme.zoo-6.1+json",'accept':"application/vnd.alertme.zoo-6.1+json",'x-omnia-client':"Hive Web Dashboard"}
        login = self._requests.post(self._url + "auth/sessions", data=payload, headers=headers)
        loginParsed = json.loads(login.text)
        token = loginParsed["sessions"][0]["sessionId"]
        self._headers = {
            'content-type': "application/vnd.alertme.zoo-6.1+json",
            'accept': "application/vnd.alertme.zoo-6.1+json",
            'x-omnia-client': "Hive Web Dashboard",
            'x-omnia-access-token': token
        }
        thermostat = self._requests.get(self._url + "nodes/*** YOUR HIVE ADDRESS ***", headers=self._headers)
        self._thermostatParsed = json.loads(thermostat.text)["nodes"][0]["attributes"]

James - your suggestion worked - many thanks for that.

I do have a problem where I am unable to view options in the operation mode list other than the one it is set to. For instanace, if schedule is set, I can only see schedule and not manual or off which means none of these can be set. Do you have a similar issue?