Skip to content
This repository has been archived by the owner on Mar 12, 2021. It is now read-only.

Commit

Permalink
✨ Add Heatzy Pilote v1 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Dramloc committed Feb 2, 2019
1 parent f24ac28 commit 45baa43
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 148 deletions.
140 changes: 27 additions & 113 deletions climate/heatzy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,26 @@
import logging

import async_timeout
from homeassistant.components.climate import (STATE_COOL, STATE_ECO,
STATE_HEAT, SUPPORT_AWAY_MODE,
SUPPORT_ON_OFF,
SUPPORT_OPERATION_MODE,
ClimateDevice)
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import aiohttp_client, storage

from .api import HeatzyAPI
from .authenticator import HeatzyAuthenticator
from .const import STORAGE_KEY, STORAGE_VERSION
from .const import (HEATZY_PILOTE_V1_PRODUCT_KEY, HEATZY_PILOTE_V2_PRODUCT_KEY,
STORAGE_KEY, STORAGE_VERSION)
from .pilote_v1 import HeatzyPiloteV1Thermostat
from .pilote_v2 import HeatzyPiloteV2Thermostat

PRODUCT_KEY_TO_DEVICE_IMPLEMENTATION = {
HEATZY_PILOTE_V1_PRODUCT_KEY: HeatzyPiloteV1Thermostat,
HEATZY_PILOTE_V2_PRODUCT_KEY: HeatzyPiloteV2Thermostat,
}

_LOGGER = logging.getLogger(__name__)

HA_TO_HEATZY_STATE = {
STATE_HEAT: 'cft',
STATE_ECO: 'eco',
STATE_COOL: 'fro'
}
HEATZY_TO_HA_STATE = {
'cft': STATE_HEAT,
'eco': STATE_ECO,
'fro': STATE_COOL,
}


async def async_setup_platform(hass, config, add_devices, discovery_info=None):
"""Configure Heatzy API using Home Assistant configuration and fetch all Heatzy devices."""
# retrieve platform config
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
Expand All @@ -41,100 +33,22 @@ async def async_setup_platform(hass, config, add_devices, discovery_info=None):

# fetch configured Heatzy devices
devices = await api.async_get_devices()
# add all Heatzy devices to home assistant
add_devices(HeatzyPilotThermostat(api, device) for device in devices)
# add all Heatzy devices with HA implementation to home assistant
add_devices(filter(None.__ne__, map(setup_heatzy_device(api), devices)))
return True


class HeatzyPilotThermostat(ClimateDevice):
def __init__(self, api, device):
self._api = api
self._device = device

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

@property
def operation_list(self):
"""Return the list of available operation modes."""
return {
STATE_HEAT,
STATE_ECO,
STATE_COOL
}

@property
def supported_features(self):
"""Return the list of supported features."""
return SUPPORT_OPERATION_MODE | SUPPORT_AWAY_MODE | SUPPORT_ON_OFF

@property
def unique_id(self):
"""Return a unique ID."""
return self._device.get('did')

@property
def name(self):
return self._device.get('dev_alias')

@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
return HEATZY_TO_HA_STATE.get(self._device.get('attr').get('mode'))

@property
def is_away_mode_on(self):
"""Return true if away mode is on."""
return self._device.get('attr').get('derog_mode') == 1

@property
def is_on(self):
"""Return true if on."""
return self._device.get('attr').get('mode') != 'stop'

async def set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
_LOGGER.debug("Setting operation mode '%s' for device '%s'...",
operation_mode, self.unique_id)
await self._api.async_set_mode(self.unique_id, HA_TO_HEATZY_STATE.get(operation_mode))
await self.async_update()
_LOGGER.info("Operation mode set to '%s' for device '%s'",
operation_mode, self.unique_id)

async def async_turn_on(self):
"""Turn device on."""
_LOGGER.debug("Turning device '%s' on...", self.unique_id)
await self._api.async_turn_on(self.unique_id)
await self.async_update()
_LOGGER.info("Device '%s' turned on (%s)", self.unique_id)

async def async_turn_off(self):
"""Turn device off."""
_LOGGER.debug("Turning device '%s' off...", self.unique_id)
await self._api.async_turn_off(self.unique_id)
await self.async_update()
_LOGGER.info("Device '%s' turned off (%s)", self.unique_id)

async def async_turn_away_mode_on(self):
"""Turn away mode on."""
_LOGGER.debug("Turning device '%s' away mode on...", self.unique_id)
await self._api.async_turn_away_mode_on(self.unique_id)
await self.async_update()
_LOGGER.info("Device '%s' away mode turned on", self.unique_id)

async def turn_away_mode_off(self):
"""Turn away mode off."""
_LOGGER.debug("Turning device '%s' away mode off...", self.unique_id)
await self._api.async_turn_away_mode_off(self.unique_id)
await self.async_update()
_LOGGER.info("Device '%s' away mode turned off", self.unique_id)

async def async_update(self):
"""Retrieve latest state."""
_LOGGER.debug("Updating device '%s'... Current state is: %s",
self.unique_id, {'is_on': self.is_on, 'is_away_mode_on': self.is_away_mode_on, 'current_operation': self.current_operation})
self._device = await self._api.async_get_device(self.unique_id)
_LOGGER.debug("Device '%s' updated. New state is: %s",
self.unique_id, {'is_on': self.is_on, 'is_away_mode_on': self.is_away_mode_on, 'current_operation': self.current_operation})
def setup_heatzy_device(api):
def find_heatzy_device_implementation(device):
"""Find Home Assistant implementation for the Heatzy device.
Implementation search is based on device 'product_key'.
If the implementation is not found, returns None.
"""
DeviceImplementation = PRODUCT_KEY_TO_DEVICE_IMPLEMENTATION.get(
device.get('product_key'))
if DeviceImplementation is None:
_LOGGER.warn('Device %s with product key %s is not supported',
device.get('did'), device.get('product_key'))
return None
return DeviceImplementation(api, device)
return find_heatzy_device_implementation
36 changes: 2 additions & 34 deletions climate/heatzy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,43 +56,11 @@ async def _async_get_device_data(self, device_id):
device_data = await response.json()
return device_data

async def _async_control_device(self, device_id, payload):
async def async_control_device(self, device_id, payload):
"""Control state of device with given id"""
token = await self._async_get_token()
headers = {
'X-Gizwits-Application-Id': HEATZY_APPLICATION_ID,
'X-Gizwits-User-Token': token
}
response = await self._session.post(HEATZY_API_URL + '/control/' + device_id, json=payload, headers=headers)

async def async_set_mode(self, device_id, mode):
"""Change device mode. Mode can be 'cft', 'eco', 'fro' or 'stop'."""
return await self._async_control_device(device_id, {
'attrs': {
'mode': mode
}
})

async def async_turn_on(self, device_id):
"""Turn device on"""
return await self.async_set_mode(device_id, 'cft')

async def async_turn_off(self, device_id):
"""Turn device off"""
return await self.async_set_mode(device_id, 'stop')

async def _async_set_derog_mode(self, device_id, derog_mode):
"""Set device 'derog_mode' (away_mode)"""
return await self._async_control_device(device_id, {
'attrs': {
'derog_mode': derog_mode
}
})

async def async_turn_away_mode_on(self, device_id):
"""Turn device away mode on"""
return await self._async_set_derog_mode(device_id, 1)

async def async_turn_away_mode_off(self, device_id):
"""Turn device away mode off"""
return await self._async_set_derog_mode(device_id, 0)
await self._session.post(HEATZY_API_URL + '/control/' + device_id, json=payload, headers=headers)
2 changes: 1 addition & 1 deletion climate/heatzy/authenticator.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async def async_authenticate(self):

if response.status != 200:
_LOGGER.error("Heatzy API returned HTTP status %d, response %s",
response.status, result)
response.status, response)
return None

authentication = await response.json()
Expand Down
2 changes: 2 additions & 0 deletions climate/heatzy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
STORAGE_KEY = 'heatzy_auth'
HEATZY_APPLICATION_ID = 'c70a66ff039d41b4a220e198b0fcc8b3'
HEATZY_API_URL = 'https://euapi.gizwits.com/app'
HEATZY_PILOTE_V1_PRODUCT_KEY = '9420ae048da545c88fc6274d204dd25f'
HEATZY_PILOTE_V2_PRODUCT_KEY = '51d16c22a5f74280bc3cfe9ebcdc6402'
82 changes: 82 additions & 0 deletions climate/heatzy/pilote_v1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from homeassistant.components.climate import (STATE_COOL, STATE_ECO,
STATE_HEAT, SUPPORT_ON_OFF,
SUPPORT_OPERATION_MODE,
ClimateDevice)
from homeassistant.const import STATE_OFF, TEMP_CELSIUS

HEATZY_TO_HA_STATE = {
'\u8212\u9002': STATE_HEAT,
'\u7ecf\u6d4e': STATE_ECO,
'\u89e3\u51bb': STATE_COOL,
'\u505c\u6b62': STATE_OFF,
}

HA_TO_HEATZY_STATE = {
STATE_HEAT: [1, 1, 0],
STATE_ECO: [1, 1, 1],
STATE_COOL: [1, 1, 2],
STATE_OFF: [1, 1, 3],
}


class HeatzyPiloteV1Thermostat(ClimateDevice):
def __init__(self, api, device):
self._api = api
self._device = device

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

@property
def operation_list(self):
"""Return the list of available operation modes."""
return {
STATE_HEAT,
STATE_ECO,
STATE_COOL
}

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

@property
def unique_id(self):
"""Return a unique ID."""
return self._device.get('did')

@property
def name(self):
return self._device.get('dev_alias')

@property
def current_operation(self):
"""Return current operation ie. heat, cool, idle."""
return HEATZY_TO_HA_STATE.get(self._device.get('attr').get('mode'))

@property
def is_on(self):
"""Return true if on."""
return self.current_operation != STATE_OFF

async def async_set_operation_mode(self, operation_mode):
"""Set new target operation mode."""
await self._api.async_control_device(self.unique_id, {
'raw': HA_TO_HEATZY_STATE.get(operation_mode),
})
await self.async_update()

async def async_turn_on(self):
"""Turn device on."""
await self._api.async_set_operation_mode(STATE_HEAT)

async def async_turn_off(self):
"""Turn device off."""
await self._api.async_set_operation_mode(STATE_OFF)

async def async_update(self):
"""Retrieve latest state."""
self._device = await self._api.async_get_device(self.unique_id)
Loading

0 comments on commit 45baa43

Please sign in to comment.