Climate control using json commands for iZone Panasonic Controller

Hi Team,

I have been trying to debug this for days but still not entirely successful. I have an Air Conditioner controller that is able to take json post requests. I have read the manual and been able to write a short python script that can control the unit and read store the system information. Once I had this working I tried to integrate it with the Broadlink IR Climate Custom Component Broadlink IR Climate Component. I have it working to a point where I can select temp and mode then see that the selected settings in the home-assistant.log.

Unfortunately, the AC Controller (iZone) is not accepting the post request when it is inside the custom_component script like it does when I use exactly the same commands in the python test script.

Here is the test code that is working perfectly. I have never learnt Python so there are likely much better ways to achieve the same result but it does work:
import json
import requests

# defining the endpoint  
onENDPOINT = ""        #{"SystemON":"x"} "on" "off"
SetPointENDPOINT = ""        #{"SystemON":"x"} "on" "off"
ModeENDPOINT = ""   # {"SystemMODE":"x"} "cool" "heat" "vent" "dry" "auto"
SettingsENDPOINT = ""

dataOn = {"SystemON":"off"}
dataTemp = {'UnitSetpoint':'23'}
dataMode = {"SystemMODE":"vent"}

# sending post request and saving response as response object 
r = = onENDPOINT, json= dataOn) 
r = = ModeENDPOINT, json= dataMode) 
r =, json= dataTemp) 


rx = 

#format AC response into new line for each parameter
loaded_json = json.loads(rx.text)
for x in loaded_json:
	print("%s: %s" % (x, loaded_json[x]))

# store each value into an object
class sysInfo(object):
def __init__(self, data):
    self.__dict__ = json.loads(rx.text)

acInfo = sysInfo(rx.text)

SystemOn = acInfo.SysOn
SetPoint = acInfo.Setpoint
FanSpeed = acInfo.SysFan


And here is the code that is only 50% working but not actually changing the state of the AC Control: The code that i have been playing with is inside the "def send_izone(self): " function.

import json
# importing the requests library 
import requests 

import asyncio
import logging
import binascii
import socket
import os.path
import voluptuous as vol
import homeassistant.helpers.config_validation as cv

from homeassistant.core import callback
from homeassistant.components.climate import (ClimateDevice, PLATFORM_SCHEMA, STATE_OFF, STATE_IDLE, STATE_HEAT, STATE_COOL, STATE_AUTO,
from homeassistant.helpers.event import (async_track_state_change)
from homeassistant.helpers.restore_state import RestoreEntity
from configparser import ConfigParser
from base64 import b64encode, b64decode

REQUIREMENTS = ['broadlink==0.9.0']

_LOGGER = logging.getLogger(__name__)


#CONF_IRCODES_INI = 'ircodes_ini'
CONF_MIN_TEMP = 'min_temp'
CONF_MAX_TEMP = 'max_temp'
CONF_TARGET_TEMP = 'target_temp'
CONF_TARGET_TEMP_STEP = 'target_temp_step'
CONF_TEMP_SENSOR = 'temp_sensor'
CONF_OPERATIONS = 'operations'
CONF_FAN_MODES = 'fan_modes'
CONF_DEFAULT_OPERATION = 'default_operation'
CONF_DEFAULT_FAN_MODE = 'default_fan_mode'

CONF_DEFAULT_OPERATION_FROM_IDLE = 'default_operation_from_idle'

DEFAULT_NAME = 'Broadlink IR Climate'
DEFAULT_FAN_MODE_LIST = ['low', 'mid', 'high', 'auto']

OnENDPOINT = ""        #{"SystemON":"x"} "on" "off"
SetPointENDPOINT = ""        #{"SystemON":"x"} "on" "off"
ModeENDPOINT = ""   # {"SystemMODE":"x"} "cool" "heat" "vent" "dry" "auto"
SettingsENDPOINT = ""

dataOn = {"SystemON":"on"}
dataTemp = {"UnitSetpoint":"23"}
dataMode = {"SystemMODE":"vent"}

    vol.Optional(CONF_OPERATIONS): vol.All(cv.ensure_list, [cv.string]),
    vol.Optional(CONF_FAN_MODES): vol.All(cv.ensure_list, [cv.string])

    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Required(CONF_HOST): cv.string,
#    vol.Required(CONF_MAC): cv.string,
#    vol.Required(CONF_IRCODES_INI): cv.string,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, 
    vol.Optional(CONF_MIN_TEMP, default=DEFAULT_MIN_TEMP): vol.Coerce(float),
    vol.Optional(CONF_MAX_TEMP, default=DEFAULT_MAX_TEMP): vol.Coerce(float),
    vol.Optional(CONF_TARGET_TEMP, default=DEFAULT_TARGET_TEMP): vol.Coerce(float),
    vol.Optional(CONF_TARGET_TEMP_STEP, default=DEFAULT_TARGET_TEMP_STEP): vol.Coerce(float),
    vol.Optional(CONF_TEMP_SENSOR): cv.entity_id,
    vol.Optional(CONF_CUSTOMIZE, default={}): CUSTOMIZE_SCHEMA,
    vol.Optional(CONF_DEFAULT_OPERATION, default=DEFAULT_OPERATION): cv.string,
    vol.Optional(CONF_DEFAULT_FAN_MODE, default=DEFAULT_FAN_MODE): cv.string,
    vol.Optional(CONF_DEFAULT_OPERATION_FROM_IDLE): cv.string

async def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
    """Set up the Broadlink IR Climate platform."""
    name = config.get(CONF_NAME)
    ip_addr = config.get(CONF_HOST)
#    mac_addr = binascii.unhexlify(config.get(CONF_MAC).encode().replace(b':', b''))
    min_temp = config.get(CONF_MIN_TEMP)
    max_temp = config.get(CONF_MAX_TEMP)
    target_temp = config.get(CONF_TARGET_TEMP)
    target_temp_step = config.get(CONF_TARGET_TEMP_STEP)
    temp_sensor_entity_id = config.get(CONF_TEMP_SENSOR)
    operation_list = config.get(CONF_CUSTOMIZE).get(CONF_OPERATIONS, []) or DEFAULT_OPERATION_LIST
    fan_list = config.get(CONF_CUSTOMIZE).get(CONF_FAN_MODES, []) or DEFAULT_FAN_MODE_LIST
    default_operation = config.get(CONF_DEFAULT_OPERATION)
    default_fan_mode = config.get(CONF_DEFAULT_FAN_MODE)
    default_operation_from_idle = config.get(CONF_DEFAULT_OPERATION_FROM_IDLE)
        BroadlinkIRClimate(hass, name, min_temp, max_temp, target_temp, target_temp_step, temp_sensor_entity_id, operation_list, fan_list, default_operation, default_fan_mode, default_operation_from_idle)

class BroadlinkIRClimate(ClimateDevice, RestoreEntity):

    def __init__(self, hass, name, min_temp, max_temp, target_temp, target_temp_step, temp_sensor_entity_id, operation_list, fan_list, default_operation, default_fan_mode, default_operation_from_idle):
        """Initialize the Broadlink IR Climate device."""
        self.hass = hass
        self._name = name

        self._min_temp = min_temp
        self._max_temp = max_temp
        self._target_temperature = target_temp
        self._target_temperature_step = target_temp_step
        self._unit_of_measurement = hass.config.units.temperature_unit
        self._current_temperature = 0
        self._temp_sensor_entity_id = temp_sensor_entity_id

        self._current_operation = default_operation
        self._current_fan_mode = default_fan_mode
        self._operation_list = operation_list
        self._fan_list = fan_list
        self._default_operation_from_idle = default_operation_from_idle
        if temp_sensor_entity_id:
                hass, temp_sensor_entity_id, self._async_temp_sensor_changed)
            sensor_state = hass.states.get(temp_sensor_entity_id)    
            if sensor_state:
    def send_izone(self):     
        _LOGGER.error("Send data to iZone")
        section = self._current_operation.lower()
        if section == 'idle':
#            value = 'off_command'
            jsonOn = {'SystemON':'off'}
            r ='', json = {'SystemON':'off'},timeout=1)

            _LOGGER.error("Sending command [%s] to %s", jsonOn, self._name)

            jsonTemp = {'UnitSetpoint':'{0:g}'.format(float(self._target_temperature))}
            jsonMode = {'SystemMODE':self._current_fan_mode.lower()}
            jsonOn = {'SystemON':'on'}
            _LOGGER.error("Sending command [%s %s] to %s", jsonTemp, jsonMode, self._name)
#            r ='', json= {'UnitSetpoint':'27'}, timeout=1)
            r =, json= jsonTemp,timeout=1)

            r = = ModeENDPOINT, json= jsonMode,timeout=1)
#        r ='', json = {'SystemON':'on'},timeout=1)
        r =, json= jsonOn, timeout=1)

        rx = 

        #format AC response into new line for each parameter
        loaded_json = json.loads(rx.text)
        for x in loaded_json:
            _LOGGER.error("%s: %s" % (x, loaded_json[x]))

        # store each value into an object
        class sysInfo(object):
            def __init__(self, data):
                self.__dict__ = json.loads(rx.text)

        acInfo = sysInfo(rx.text)
        SystemOn = acInfo.SysOn
        SetPoint = acInfo.Setpoint
        FanSpeed = acInfo.SysFan
        Mode = acInfo.SysMode

    async def _async_temp_sensor_changed(self, entity_id, old_state, new_state):
        """Handle temperature changes."""
        if new_state is None:

        await self.async_update_ha_state()
    def _async_update_current_temp(self, state):
        """Update thermostat with latest state from sensor."""
        unit = state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)

            _state = state.state
            if self.represents_float(_state):
                self._current_temperature = self.hass.config.units.temperature(
                    float(_state), unit)
        except ValueError as ex:
            _LOGGER.error('Unable to update from sensor: %s', ex)    

    def represents_float(self, s):
            return True
        except ValueError:
            return False     

    def should_poll(self):
        """Return the polling state."""
        return False

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

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

    def current_temperature(self):
        """Return the current temperature."""
        return self._current_temperature
    def min_temp(self):
        """Return the polling state."""
        return self._min_temp
    def max_temp(self):
        """Return the polling state."""
        return self._max_temp    
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._target_temperature
    def target_temperature_step(self):
        """Return the supported step of target temperature."""
        return self._target_temperature_step

    def current_operation(self):
        """Return current operation ie. heat, cool, idle."""
        return self._current_operation

    def operation_list(self):
        """Return the list of available operation modes."""
        return self._operation_list

    def current_fan_mode(self):
        """Return the fan setting."""
        return self._current_fan_mode

    def fan_list(self):
        """Return the list of available fan modes."""
        return self._fan_list
    def supported_features(self):
        """Return the list of supported features."""
        return SUPPORT_FLAGS        
    def set_temperature(self, **kwargs):
        """Set new target temperatures."""
        if kwargs.get(ATTR_TEMPERATURE) is not None:
            self._target_temperature = kwargs.get(ATTR_TEMPERATURE)
            if not (self._current_operation.lower() == 'off' or self._current_operation.lower() == 'idle'):
            elif self._default_operation_from_idle is not None:

    def set_fan_mode(self, fan):
        """Set new target temperature."""
        self._current_fan_mode = fan
        if not (self._current_operation.lower() == 'off' or self._current_operation.lower() == 'idle'):

    def set_operation_mode(self, operation_mode):
        """Set new target temperature."""
        self._current_operation = operation_mode

    async def async_added_to_hass(self):
        """Run when entity about to be added."""
        await super().async_added_to_hass()
        last_state = await self.async_get_last_state()
        if last_state is not None:
            self._target_temperature = last_state.attributes['temperature']
            self._current_operation = last_state.attributes['operation_mode']
            self._current_fan_mode = last_state.attributes['fan_mode']

Thanks in anticipation and hopefully this might also help someone else once it is solved!

Also: the lines 174 to 194 seem to be working perfectly and pull data from the AC controller. It is just getting the controller to accept the “r =, json= jsonTemp,timeout=1)” that seems to be causing the bigest problem.

So I just tested the Test code using Python3 rather than Python and interestingly it did not work, and responded exactly the same as my modified custom component. So… I think i already knew HA is using Python3 but it did not occur to me that I was testing the test script with Python2.

Could anyone help me adjust the code so that it will work with Python3? I think that would solve my problem.


Can’t help right now unfortunately, but I’ll be keeping a keen eye, this is exactly the setup I’ll have in the near-ish future. If you’re not done in 6 months I’ll have a go :slight_smile:

Edit: Rather than doing it all from scratch, have you tried using the IZone python module and just writing the middleware to HA?

Further edit: existing component by the same author:

Well done callifo! that did not exist when i started this project which is why I didn’t find it my self. I should have done some googling when I picked up the project again. I have just loading the custom component. No success yet but i will let you know if i get either version working.

Hi @callifo,

I tried Swamp iZone component but it did not work for my setup without having Python 3.6 or higher. Should work on HASS.IO but have not had an oppertunity to test.

Meanwhile the guys at iZone got back to me with some alternate code that worked well for me.

Certainly not the best solution but this is currently working well for me:

I would be happy for it to be developed further if you are keen.