ee02217 / homeassistant-mi-heater

Home Assistant integration for MiHeaters.
59 stars 16 forks source link

Not work on zhimi.heater.za1 #6

Open leva0887 opened 3 years ago

leva0887 commented 3 years ago

Logger: custom_components.miheater.climate Source: /config/custom_components/miheater/climate.py:191 Integration: miheater (documentation) First occurred: 12:48:44 PM (3 occurrences) Last logged: 12:48:44 PM

Unsupported model: zhimi.heater.za1 NoneType: None

Logger: homeassistant.components.climate Source: custom_components/miheater/climate.py:193 Integration: Climate (documentation, issues) First occurred: 12:48:44 PM (3 occurrences) Last logged: 12:48:44 PM

Error while setting up miheater platform for climate Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 199, in _async_setup_platform await asyncio.shield(task) File "/usr/local/lib/python3.8/concurrent/futures/thread.py", line 57, in run result = self.fn(*self.args, **self.kwargs) File "/config/custom_components/miheater/climate.py", line 80, in setup_platform miHeater = MiHeater(device, name, model, unique_id, hass) File "/config/custom_components/miheater/climate.py", line 116, in init self.getAttrData() File "/config/custom_components/miheater/climate.py", line 193, in getAttrData data['power'] = power[0]["value"] UnboundLocalError: local variable 'power' referenced before assignment

ee02217 commented 3 years ago

Hi, That model is in fact unsupported by my code. If someone wishes to extend it and create a pull request, I would be more that happy :)

sygys commented 3 years ago

zb1 isnt showing up also. i guess the code isnt working at all anymore. i retreived the token put in the ip and name in the configuration file installed the custom component en rebooted Hass. no error but heater isnt showing up in hass. I think the code is no longer working. the whole integration in HACS cant be found also. only a chinese version. so maybe this is discontinued

ee02217 commented 3 years ago

I saw your comment in HA community that it’s working with your version, so I’m gonna keep that one as supported. am I right?

sygys commented 3 years ago

YES! its working. What i did see is that when you activate the heater within HA and it reaches its target temperature it turns itself off and not on anymore when the temperature has dropped. So i think its a seperate way of turning on the heater?

ee02217 commented 3 years ago

That is weird. It doesn’t happen with my version :(

sygys commented 3 years ago

So normally the heater would turn off when a temp has reached and back on when it drops below that temp right?

ee02217 commented 3 years ago

In my case, with Xiaomi Space Heater S, when it reaches the temperature it just reduces power, does not turn off, to maintain the desired temperature. I can see this power change via a smart plug. This happens regardless if I configure it via HA or via Xiaomi App.

droserHA commented 2 years ago

Hi, That model is in fact unsupported by my code. If someone wishes to extend it and create a pull request, I would be more that happy :)

What we need to do so that you add to ZA1 code?Thank you!

ee02217 commented 2 years ago

Well… if I had the heater, I would try myself. As an alternative, the code is open, and someone who has it, can modify the code and include support for it.

sygys commented 2 years ago

I have been using the code and sending commands to node red. But I too don't have the za1. I saw on your code that you use raw command to send the commands. How did you manage to get the commands for the heater?

droserHA commented 2 years ago

Well… if I had the heater, I would try myself. As an alternative, the code is open, and someone who has it, can modify the code and include support for it.

I am ready to try, but I am not familiar with Python.Where and what you need to change?

sygys commented 2 years ago

in climate.py search for "self._device.raw_command" the info behind it are the commands send to the heater. The only thing now to do is find out what the commands for your heater are... like i asked before we need to know how @ee02217 did manage to get the commands out. As soon as you know what the commands need to be you can start adding them.

sygys commented 2 years ago

In node red i manages to turn the zb1 on by sending this code to it:

{
    "cmd": "miio",
    "method": "set_properties",
    "value": [
        {
            "value": true,
            "siid": 2,
            "piid": 2
        }
    ]
}

and off is this command:

{
    "cmd": "miio",
    "method": "set_properties",
    "value": [
        {
            "value": false,
            "siid": 2,
            "piid": 2
        }
    ]
}

There is also the get_properties ability with different commands like list. But i seem to get an error. So im not sure how to ask the heater to give it info.

It would be great if ee02217 is willing to share how he did the reverse engineering. here is some info on how to retreive data: https://github.com/rytilahti/python-miio

sygys commented 2 years ago

And this is a very important site which explains how to issue commands. I had allot of help on that one. but its all in chinese so just look at the commands: https://github.com/YinHangCode/node-red-contrib-mi-miio

The reason i want to control the heater without the integration is because it somehow lags HA when its running. I want to remove the integration and do all this in node red.

When doing the list command, the problem is i get an emty array so i guess this option isnt supported by the heater. Knipsel

ee02217 commented 2 years ago

I got this from this url: https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:heater:0000A01A:zhimi-za1:1

got there from a lot of googling :)

sygys commented 2 years ago

i can imagine that! wow this is well hided. And the normal url witout the extras wont get you anywhere so you really need the whole link. when you change "A:zhimi-za1:1" to "A:zhimi-zb1:1" i see i get my heater

sygys commented 2 years ago

to make it more readable:

{
    "type": "urn:miot-spec-v2:device:heater:0000A01A:zhimi-za1:1",
    "description": "Heater",
    "services": [
        {
            "iid": 1,
            "type": "urn:miot-spec-v2:service:device-information:00007801:zhimi-za1:1",
            "description": "Device Information",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:manufacturer:00000001:zhimi-za1:1",
                    "description": "Device Manufacturer",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:model:00000002:zhimi-za1:1",
                    "description": "Device Model",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 3,
                    "type": "urn:miot-spec-v2:property:serial-number:00000003:zhimi-za1:1",
                    "description": "Device Serial Number",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 4,
                    "type": "urn:miot-spec-v2:property:firmware-revision:00000005:zhimi-za1:1",
                    "description": "Current Firmware Version",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                }
            ]
        },
        {
            "iid": 2,
            "type": "urn:miot-spec-v2:service:heater:0000782E:zhimi-za1:1",
            "description": "Heater",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:on:00000006:zhimi-za1:1",
                    "description": "Switch Status",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:target-temperature:00000021:zhimi-za1:1",
                    "description": "Target Temperature",
                    "format": "float",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ],
                    "unit": "celsius",
                    "value-range": [
                        16,
                        32,
                        1
                    ]
                }
            ]
        },
        {
            "iid": 3,
            "type": "urn:miot-spec-v2:service:environment:0000780A:zhimi-za1:1",
            "description": "Environment",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:temperature:00000020:zhimi-za1:1",
                    "description": "Temperature",
                    "format": "float",
                    "access": [
                        "read",
                        "notify"
                    ],
                    "unit": "celsius",
                    "value-range": [
                        -40,
                        125,
                        0.1
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:relative-humidity:0000000C:zhimi-za1:1",
                    "description": "Relative Humidity",
                    "format": "uint8",
                    "access": [
                        "read",
                        "notify"
                    ],
                    "unit": "percentage",
                    "value-range": [
                        0,
                        100,
                        1
                    ]
                }
            ]
        },
        {
            "iid": 4,
            "type": "urn:miot-spec-v2:service:physical-controls-locked:00007807:zhimi-za1:1",
            "description": "Physical Control Locked",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:physical-controls-locked:0000001D:zhimi-za1:1",
                    "description": "Physical Control Locked",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                }
            ]
        },
        {
            "iid": 5,
            "type": "urn:miot-spec-v2:service:alarm:00007804:zhimi-za1:1",
            "description": "Alarm",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:alarm:00000012:zhimi-za1:1",
                    "description": "Alarm",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                }
            ]
        }
    ]
}
sygys commented 2 years ago

For getting the temperature i need those 2 values: "siid": 2, "piid": 6

When looking in the code i only see iid: 6

Where does the p stands for? properties? and s in siid seem to stand for services. How did you manage to find that out :)

Man you have put in allot of work just trying to find out the commands alone lol

But reading this controlling the za1 shouldnt be that big of a deal now. atleast not when importing it in Node red.

I removed the integration and no more freezes anymore so i still think the smartmi integration causes some serious lag behind the scenes.

ee02217 commented 2 years ago

True… it was a lot of trial and error… covid got me stuck at home… I picked other existing integrations and just started changing values.

droserHA commented 2 years ago

to make it more readable:

{
    "type": "urn:miot-spec-v2:device:heater:0000A01A:zhimi-za1:1",
    "description": "Heater",
    "services": [
        {
            "iid": 1,
            "type": "urn:miot-spec-v2:service:device-information:00007801:zhimi-za1:1",
            "description": "Device Information",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:manufacturer:00000001:zhimi-za1:1",
                    "description": "Device Manufacturer",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:model:00000002:zhimi-za1:1",
                    "description": "Device Model",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 3,
                    "type": "urn:miot-spec-v2:property:serial-number:00000003:zhimi-za1:1",
                    "description": "Device Serial Number",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                },
                {
                    "iid": 4,
                    "type": "urn:miot-spec-v2:property:firmware-revision:00000005:zhimi-za1:1",
                    "description": "Current Firmware Version",
                    "format": "string",
                    "access": [
                        "read"
                    ]
                }
            ]
        },
        {
            "iid": 2,
            "type": "urn:miot-spec-v2:service:heater:0000782E:zhimi-za1:1",
            "description": "Heater",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:on:00000006:zhimi-za1:1",
                    "description": "Switch Status",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:target-temperature:00000021:zhimi-za1:1",
                    "description": "Target Temperature",
                    "format": "float",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ],
                    "unit": "celsius",
                    "value-range": [
                        16,
                        32,
                        1
                    ]
                }
            ]
        },
        {
            "iid": 3,
            "type": "urn:miot-spec-v2:service:environment:0000780A:zhimi-za1:1",
            "description": "Environment",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:temperature:00000020:zhimi-za1:1",
                    "description": "Temperature",
                    "format": "float",
                    "access": [
                        "read",
                        "notify"
                    ],
                    "unit": "celsius",
                    "value-range": [
                        -40,
                        125,
                        0.1
                    ]
                },
                {
                    "iid": 2,
                    "type": "urn:miot-spec-v2:property:relative-humidity:0000000C:zhimi-za1:1",
                    "description": "Relative Humidity",
                    "format": "uint8",
                    "access": [
                        "read",
                        "notify"
                    ],
                    "unit": "percentage",
                    "value-range": [
                        0,
                        100,
                        1
                    ]
                }
            ]
        },
        {
            "iid": 4,
            "type": "urn:miot-spec-v2:service:physical-controls-locked:00007807:zhimi-za1:1",
            "description": "Physical Control Locked",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:physical-controls-locked:0000001D:zhimi-za1:1",
                    "description": "Physical Control Locked",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                }
            ]
        },
        {
            "iid": 5,
            "type": "urn:miot-spec-v2:service:alarm:00007804:zhimi-za1:1",
            "description": "Alarm",
            "properties": [
                {
                    "iid": 1,
                    "type": "urn:miot-spec-v2:property:alarm:00000012:zhimi-za1:1",
                    "description": "Alarm",
                    "format": "bool",
                    "access": [
                        "read",
                        "write",
                        "notify"
                    ]
                }
            ]
        }
    ]
}

this code is for whom? where to insert it?

ee02217 commented 2 years ago

That is not code. Those are just the values needed to use in the code.

sygys commented 2 years ago

@ee02217 Is this just as easy as adding this to the code to make it work for za1:

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":2}])

added part

        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1}])

end of added part

        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)

and do this for all the raw commands in your climate.py

This is 5 minutes of work to get it working for the za1

If im correct then above i just added support for the za1 to turn it off

sygys commented 2 years ago

I could help with this adding all these lines to the code. But i need to be sure its correct and the only thing that needs to be done. I dont want to break anyone's config...

sygys commented 2 years ago

@droserHA This should be the code to put in climate.py for support of the zhimi.heater.za1

Make a backup before you use this. i have no experience in making integrations only changed the code in which i think it should work. Maybe @ee02217 can do a last check.

You need to check what your minimum temp of the heater is. if its not 16 then the code need another change. Let me know and dont use this code before you checked that.

"""
    Support for Xiaomi wifi-enabled home heaters via miio.
    author: sunfang1cn@gmail.com
    modifier: ee02217
    Tested environment: HASS 0.118.5
"""
import logging

import voluptuous as vol

from homeassistant.components.climate import ClimateEntity, PLATFORM_SCHEMA
from homeassistant.components.climate.const import (
    DOMAIN, HVAC_MODE_HEAT,HVAC_MODE_COOL, HVAC_MODE_OFF,
    SUPPORT_TARGET_TEMPERATURE, SUPPORT_FAN_MODE)
from homeassistant.const import (
    ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, CONF_TOKEN, CONF_DEVICE_ID,
    STATE_ON, STATE_OFF, TEMP_CELSIUS)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.exceptions import PlatformNotReady

from miio import Device,DeviceException

_LOGGER = logging.getLogger(__name__)

CONF_MODEL = 'model'
REQUIREMENTS = ['python-miio>=0.5.0']
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE)
SERVICE_SET_ROOM_TEMP = 'miheater_set_room_temperature'
PRECISION = 1
MIN_TEMP = 18
MIN_TEMP_ZB1 = 16
MAX_TEMP = 28
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Required(CONF_NAME): cv.string,
    vol.Required(CONF_TOKEN): cv.string,
    vol.Optional(CONF_DEVICE_ID): cv.string,
    vol.Optional(CONF_MODEL, default=None): vol.In(
    ['zhimi.heater.mc2',
     'zhimi.heater.zb1',
     'zhimi.heater.za2',
     'zhimi.heater.za1', None]),
})

SET_ROOM_TEMP_SCHEMA = vol.Schema({
    vol.Optional('temperature'): cv.positive_int
})

DEVICE_MODEL = ""
ATTR_MODEL = 'model'
DEVICE_ID = ""

def setup_platform(hass, config, add_devices, discovery_info=None):
    """Perform the setup for Xiaomi heaters."""
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)
    token = config.get(CONF_TOKEN)
    model = config.get(CONF_MODEL)
    DEVICE_ID = config.get(CONF_DEVICE_ID)

    _LOGGER.info("Initializing Xiaomi heaters with host %s (token %s...)", host, token[:5])

    devices = []
    unique_id = None

    try:
        device = Device(host, token)
        device_info = device.info()

        if model is None:

            model = device_info.model
            DEVICE_MODEL = model

        unique_id = "{}-{}".format(model, device_info.mac_address)
        _LOGGER.warning("%s %s %s detected",
                     model,
                     device_info.firmware_version,
                     device_info.hardware_version)
        miHeater = MiHeater(device, name, model, unique_id, hass)
        devices.append(miHeater)
        add_devices(devices)

        async def set_room_temp(service):
            """Set room temp."""

            if DEVICE_MODEL == "zhimi.heater.mc2":
                aux = device.raw_command('get_properties', [{"siid":2,"piid":5,"did":DEVICE_ID}])
            elif DEVICE_MODEL == "zhimi.heater.zb1" or DEVICE_MODEL == "zhimi.heater.za2":
                aux = device.raw_command('get_properties', [{"siid":2,"piid":6}])
            elif DEVICE_MODEL == "zhimi.heater.za1":
                aux = device.raw_command('get_properties', [{"siid":3,"piid":1}])
            else  :  
                _LOGGER.exception('Unsupported model: %s', DEVICE_MODEL)

            temperature=aux[0]["value"]
            await miHeater.async_set_temperature(temperature)

        hass.services.async_register(DOMAIN, SERVICE_SET_ROOM_TEMP,
                                     set_room_temp, schema=SET_ROOM_TEMP_SCHEMA)
    except DeviceException:
        _LOGGER.exception('Fail to setup Xiaomi heater')
        raise PlatformNotReady

class MiHeater(ClimateEntity):
    """Representation of a MiHeater device."""

    def __init__(self, device, name, model, unique_id, _hass):
        """Initialize the heater."""
        self._device = device
        self._name = name
        self._model = model
        self._state = None
        self.entity_id = generate_entity_id('climate.{}', unique_id, hass=_hass)
        self.getAttrData()
    @property
    def name(self):
        """Return the name of the device."""
        return self._name

    @property
    def device(self):
        """Return the model of the device."""
        return self._model

    @property
    def hvac_mode(self):
        return HVAC_MODE_HEAT if self._state['power'] else HVAC_MODE_OFF

    @property
    def hvac_modes(self):
        return [HVAC_MODE_HEAT, HVAC_MODE_OFF]

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

    # @property
    # def precision(self):
    #     """Return the unit of measurement which this thermostat uses."""
    #     return PRECISION

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._state['target_temperature']

    @property
    def current_temperature(self):
        """Return the current temperature."""
        return self._state['current_temperature']

    @property
    def current_humidity(self):
        """Return the current humidity."""
        return self._state['humidity']

    @property
    def target_temperature_step(self):
        """Return the supported step of target temperature."""
        return 1
    def getAttrData(self):

        try:
            data = {}

            #device_info = self._device.info()
            #DEVICE_MODEL = device_info.model

            if self._model == "zhimi.heater.mc2":
                power=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":2,"piid":1}])
                target_temperature=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":2,"piid":5}])
                current_temperature=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":4,"piid":7}])
                data['humidity']  = 0
            elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
                power=self._device.raw_command('get_properties', [{"siid":2,"piid":2}])
                humidity=self._device.raw_command('get_properties', [{"siid":5,"piid":7}])
                target_temperature=self._device.raw_command('get_properties', [{"siid":2,"piid":6}])
                current_temperature=self._device.raw_command('get_properties', [{"siid":5,"piid":8}])
                data['humidity'] = humidity[0]["value"]
            elif self._model == "zhimi.heater.za1" :
                power=self._device.raw_command('get_properties', [{"siid":2,"piid":1}])
                humidity=self._device.raw_command('get_properties', [{"siid":3,"piid":2}])
                target_temperature=self._device.raw_command('get_properties', [{"siid":2,"piid":2}])
                current_temperature=self._device.raw_command('get_properties', [{"siid":3,"piid":1}])
                data['humidity'] = humidity[0]["value"]
            else:  
                _LOGGER.exception('Unsupported model: %s', self._model)

            data['power'] = power[0]["value"]

            data['target_temperature'] = target_temperature[0]["value"]
            data['current_temperature'] = current_temperature[0]["value"]
            self._state = data
        except DeviceException:
            _LOGGER.exception('Fail to get_prop from Xiaomi heater')
            self._state = None
            raise PlatformNotReady

    @property
    def device_state_attributes(self):
        return self._state

    @property
    def is_on(self):
        """Return true if heater is on."""
        return self._state['power']

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        if self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" or self._model == "zhimi.heater.za1" :
            return MIN_TEMP_ZB1 
        else:
            return MIN_TEMP

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

    async def async_set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        _LOGGER.warning("Setting temperature: %s", int(temperature))
        if temperature is None:
            _LOGGER.error("Wrong temperature: %s", temperature)
            return

        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":5, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":6}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":2}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)

    async def async_turn_on(self):
        """Turn Mill unit on."""

        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":1, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":2}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":1}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)        

    async def async_turn_off(self):
        """Turn Mill unit off."""
        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":2}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)    

    async def async_update(self):
        """Retrieve latest state."""
        self.getAttrData()

    async def async_set_hvac_mode(self, hvac_mode):
        """Set operation mode."""
        if hvac_mode  == HVAC_MODE_HEAT or hvac_mode  == HVAC_MODE_COOL:
            await self.async_turn_on()
        elif hvac_mode  == HVAC_MODE_OFF:
            await self.async_turn_off()
        else:
            _LOGGER.error("Unrecognized operation mode: %s", hvac_mode)
droserHA commented 2 years ago

I have za1 and mihome with brakes and servers in china. I can participate in testing with my za1.Отправлено с устройства Galaxy -------- Исходное сообщение --------От: sygys @.> Дата: 29.09.2021 09:40 (GMT+02:00) Кому: ee02217/homeassistant-mi-heater @.> Копия: droserHA @.>, Comment @.> Тема: Re: [ee02217/homeassistant-mi-heater] Not work on zhimi.heater.za1 (#6) I could help with this adding all these lines to the code. But i need to be sure its correct and the only thing that needs to be done. I dont want to break anyone's config...

—You are receiving this because you commented.Reply to this email directly, view it on GitHub, or unsubscribe.Triage notifications on the go with GitHub Mobile for iOS or Android.

sygys commented 2 years ago

First check the minimum temp the heater can be set on. if its 16 the code should work.... it doesnt care which server your on. you are going to command the heater locally you do need the token.

I allready added all the lines in the code that you should need for your heater.

If this confirms to work @ee02217 can edit the code so everyone can use it.

ee02217 commented 2 years ago

First check the minimum temp the heater can be set on. if its 16 the code should work.... it doesnt care which server your on. you are going to command the heater locally you do need the token.

I allready added all the lines in the code that you should need for your heater.

If this confirms to work @ee02217 can edit the code so everyone can use it.

Hi @sygys , You can directly change the code. I made it public for that. Please change ir directly and make a merge request ;)

I’m away from the PC for a while and this way you guys can test it sooner.

sygys commented 2 years ago

No problem first i need to know if it all works then i will do a merge request... first time doing all this so bare with me if it doesnt work...

sygys commented 2 years ago

besides all this i need to try and find out whats causing the lag in home assistant filesystem when this integration is active. Since i removed it i dont have any lag in my home assistant. Something in the climate.py code is causing home assistant to freeze every few seconds. Im not really sure its only the zb1 heater part or what is causing it. i will dive into the code and test it on a spare home assistant server soon. maybe i can find out what is causing the freezes.

This is why i removed this integration for now and using node red to turn the heater on and off

ee02217 commented 2 years ago

Wow. To be honest, I haven’t felt that issue, but I a, running it in a nuc, so it has a lot of power, which may be why I didn’t find it. If you find any issue it would be great.

PS- this was my first python programming, so it may be some sort of Frankenstein code :D

sygys commented 2 years ago

Im running home assistant on a i7 server pc with a very fast ssd. so i dont think that could be it. Its not that home assistant is slow in use. but scrolling through files using the file editor is. Sometimes when opening a yaml file it takes a few seconds. also saving files sometimes has a hickup and it takes a few seconds for the pop up file saved to show up. I cant really figure it out but i also have a feeling automations sometimes had a hickup. lights turning on a few seconds later through motion then expected. and all that kind of stuff. First i thought it was my zigbee network or coordinator being slow. but since i stopped using the integration it has been very stable. And everything fires instantly so i really think its this integration..

I will test it. and report back if i find anything. thanks so far for everything. Thanks to you i am beginning to understand how all this works.

sygys commented 2 years ago

What i was wondering is where the update interval is set? Or does home assistant set this itself? When using the integration i had allot of errors: Fail to get_prop from Xiaomi heater.

I want to find out why i get these errors.

ee02217 commented 2 years ago

I found nothing in other codes related to that. So I assume HA has come default for these.

droserHA commented 2 years ago

@droserHA This should be the code to put in climate.py for support of the zhimi.heater.za1

Make a backup before you use this. i have no experience in making integrations only changed the code in which i think it should work. Maybe @ee02217 can do a last check.

You need to check what your minimum temp of the heater is. if its not 16 then the code need another change. Let me know and dont use this code before you checked that.


"""
    Support for Xiaomi wifi-enabled home heaters via miio.
    author: sunfang1cn@gmail.com
    modifier: ee02217
    Tested environment: HASS 0.118.5
"""
import logging

import voluptuous as vol

from homeassistant.components.climate import ClimateEntity, PLATFORM_SCHEMA
from homeassistant.components.climate.const import (
    DOMAIN, HVAC_MODE_HEAT,HVAC_MODE_COOL, HVAC_MODE_OFF,
    SUPPORT_TARGET_TEMPERATURE, SUPPORT_FAN_MODE)
from homeassistant.const import (
    ATTR_TEMPERATURE, CONF_HOST, CONF_NAME, CONF_TOKEN, CONF_DEVICE_ID,
    STATE_ON, STATE_OFF, TEMP_CELSIUS)
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.exceptions import PlatformNotReady

from miio import Device,DeviceException

_LOGGER = logging.getLogger(__name__)

CONF_MODEL = 'model'
REQUIREMENTS = ['python-miio>=0.5.0']
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE)
SERVICE_SET_ROOM_TEMP = 'miheater_set_room_temperature'
PRECISION = 1
MIN_TEMP = 18
MIN_TEMP_ZB1 = 16
MAX_TEMP = 28
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Required(CONF_NAME): cv.string,
    vol.Required(CONF_TOKEN): cv.string,
    vol.Optional(CONF_DEVICE_ID): cv.string,
    vol.Optional(CONF_MODEL, default=None): vol.In(
    ['zhimi.heater.mc2',
     'zhimi.heater.zb1',
     'zhimi.heater.za2',
     'zhimi.heater.za1', None]),
})

SET_ROOM_TEMP_SCHEMA = vol.Schema({
    vol.Optional('temperature'): cv.positive_int
})

DEVICE_MODEL = ""
ATTR_MODEL = 'model'
DEVICE_ID = ""

def setup_platform(hass, config, add_devices, discovery_info=None):
    """Perform the setup for Xiaomi heaters."""
    host = config.get(CONF_HOST)
    name = config.get(CONF_NAME)
    token = config.get(CONF_TOKEN)
    model = config.get(CONF_MODEL)
    DEVICE_ID = config.get(CONF_DEVICE_ID)

    _LOGGER.info("Initializing Xiaomi heaters with host %s (token %s...)", host, token[:5])

    devices = []
    unique_id = None

    try:
        device = Device(host, token)
        device_info = device.info()

        if model is None:

            model = device_info.model
            DEVICE_MODEL = model

        unique_id = "{}-{}".format(model, device_info.mac_address)
        _LOGGER.warning("%s %s %s detected",
                     model,
                     device_info.firmware_version,
                     device_info.hardware_version)
        miHeater = MiHeater(device, name, model, unique_id, hass)
        devices.append(miHeater)
        add_devices(devices)

        async def set_room_temp(service):
            """Set room temp."""

            if DEVICE_MODEL == "zhimi.heater.mc2":
                aux = device.raw_command('get_properties', [{"siid":2,"piid":5,"did":DEVICE_ID}])
            elif DEVICE_MODEL == "zhimi.heater.zb1" or DEVICE_MODEL == "zhimi.heater.za2":
                aux = device.raw_command('get_properties', [{"siid":2,"piid":6}])
            elif DEVICE_MODEL == "zhimi.heater.za1":
                aux = device.raw_command('get_properties', [{"siid":3,"piid":1}])
            else  :  
                _LOGGER.exception('Unsupported model: %s', DEVICE_MODEL)

            temperature=aux[0]["value"]
            await miHeater.async_set_temperature(temperature)

        hass.services.async_register(DOMAIN, SERVICE_SET_ROOM_TEMP,
                                     set_room_temp, schema=SET_ROOM_TEMP_SCHEMA)
    except DeviceException:
        _LOGGER.exception('Fail to setup Xiaomi heater')
        raise PlatformNotReady

class MiHeater(ClimateEntity):
    """Representation of a MiHeater device."""

    def __init__(self, device, name, model, unique_id, _hass):
        """Initialize the heater."""
        self._device = device
        self._name = name
        self._model = model
        self._state = None
        self.entity_id = generate_entity_id('climate.{}', unique_id, hass=_hass)
        self.getAttrData()
    @property
    def name(self):
        """Return the name of the device."""
        return self._name

    @property
    def device(self):
        """Return the model of the device."""
        return self._model

    @property
    def hvac_mode(self):
        return HVAC_MODE_HEAT if self._state['power'] else HVAC_MODE_OFF

    @property
    def hvac_modes(self):
        return [HVAC_MODE_HEAT, HVAC_MODE_OFF]

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

    # @property
    # def precision(self):
    #     """Return the unit of measurement which this thermostat uses."""
    #     return PRECISION

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        return self._state['target_temperature']

    @property
    def current_temperature(self):
        """Return the current temperature."""
        return self._state['current_temperature']

    @property
    def current_humidity(self):
        """Return the current humidity."""
        return self._state['humidity']

    @property
    def target_temperature_step(self):
        """Return the supported step of target temperature."""
        return 1
    def getAttrData(self):

        try:
            data = {}

            #device_info = self._device.info()
            #DEVICE_MODEL = device_info.model

            if self._model == "zhimi.heater.mc2":
                power=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":2,"piid":1}])
                target_temperature=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":2,"piid":5}])
                current_temperature=self._device.raw_command('get_properties', [{"did":DEVICE_ID,"siid":4,"piid":7}])
                data['humidity']  = 0
            elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
                power=self._device.raw_command('get_properties', [{"siid":2,"piid":2}])
                humidity=self._device.raw_command('get_properties', [{"siid":5,"piid":7}])
                target_temperature=self._device.raw_command('get_properties', [{"siid":2,"piid":6}])
                current_temperature=self._device.raw_command('get_properties', [{"siid":5,"piid":8}])
                data['humidity'] = humidity[0]["value"]
            elif self._model == "zhimi.heater.za1" :
                power=self._device.raw_command('get_properties', [{"siid":2,"piid":1}])
                humidity=self._device.raw_command('get_properties', [{"siid":3,"piid":2}])
                target_temperature=self._device.raw_command('get_properties', [{"siid":2,"piid":2}])
                current_temperature=self._device.raw_command('get_properties', [{"siid":3,"piid":1}])
                data['humidity'] = humidity[0]["value"]
            else:  
                _LOGGER.exception('Unsupported model: %s', self._model)

            data['power'] = power[0]["value"]

            data['target_temperature'] = target_temperature[0]["value"]
            data['current_temperature'] = current_temperature[0]["value"]
            self._state = data
        except DeviceException:
            _LOGGER.exception('Fail to get_prop from Xiaomi heater')
            self._state = None
            raise PlatformNotReady

    @property
    def device_state_attributes(self):
        return self._state

    @property
    def is_on(self):
        """Return true if heater is on."""
        return self._state['power']

    @property
    def min_temp(self):
        """Return the minimum temperature."""
        if self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" or self._model == "zhimi.heater.za1" :
            return MIN_TEMP_ZB1 
        else:
            return MIN_TEMP

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

    async def async_set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        _LOGGER.warning("Setting temperature: %s", int(temperature))
        if temperature is None:
            _LOGGER.error("Wrong temperature: %s", temperature)
            return

        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":5, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":6}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":int(temperature),"siid":2,"piid":2}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)

    async def async_turn_on(self):
        """Turn Mill unit on."""

        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":1, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":2}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":True,"siid":2,"piid":1}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)        

    async def async_turn_off(self):
        """Turn Mill unit off."""
        #device_info = self._device.info()
        #DEVICE_MODEL = device_info.model      

        if self._model == "zhimi.heater.mc2":              
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1, "did":DEVICE_ID}])
        elif self._model == "zhimi.heater.zb1" or self._model == "zhimi.heater.za2" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":2}])
        elif self._model == "zhimi.heater.za1" :
            self._device.raw_command('set_properties',[{"value":False,"siid":2,"piid":1}])
        else:  
            _LOGGER.exception('Unsupported model: %s', self._model)    

    async def async_update(self):
        """Retrieve latest state."""
        self.getAttrData()

    async def async_set_hvac_mode(self, hvac_mode):
        """Set operation mode."""
        if hvac_mode  == HVAC_MODE_HEAT or hvac_mode  == HVAC_MODE_COOL:
            await self.async_turn_on()
        elif hvac_mode  == HVAC_MODE_OFF:
            await self.async_turn_off()
        else:
            _LOGGER.error("Unrecognized operation mode: %s", hvac_mode)

I inserted the code into the climate.py. But HA never did. Here are the logs This error originated from a custom integration.

Logger: custom_components.miheater.climate Source: custom_components/miheater/climate.py:80 Integration: miheater (documentation) First occurred: 1:08:50 PM (1 occurrences) Last logged: 1:08:50 PM

zhimi.heater.za1 2.2.1 esp32 detected

Logger: homeassistant.components.climate Source: custom_components/miheater/climate.py:200 Integration: climate (documentation, issues) First occurred: 1:08:52 PM (1 occurrences) Last logged: 1:08:52 PM

Error while setting up miheater platform for climate Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 249, in _async_setup_platform await asyncio.shield(task) File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 52, in run result = self.fn(*self.args, **self.kwargs) File "/config/custom_components/miheater/climate.py", line 84, in setup_platform miHeater = MiHeater(device, name, model, unique_id, hass) File "/config/custom_components/miheater/climate.py", line 122, in init self.getAttrData() File "/config/custom_components/miheater/climate.py", line 200, in getAttrData data['humidity'] = humidity[0]["value"] KeyError: 'value'

my config: climate:

sygys commented 2 years ago

I guess the problem is in the humidity part. does your heater return humidity in the xiaomi app?

sygys commented 2 years ago

try replacing data['humidity'] = humidity[0]["value"] with data['humidity'] = 0

ee02217 commented 2 years ago

That's the thing, my heater does not have any humidity sensors.

sygys commented 2 years ago

maybe this:

    @property
    def current_humidity(self):
        """Return the current humidity."""
        return self._state['humidity']

can be removed completely if you dont use it right? But you had it in the code for when some one uses the zb1 offcourse

ee02217 commented 2 years ago

Yeah, that one was included by someone to add support for it. If it causes problems, we can remove it.

I created a beta version with your code (1.6.1) if you want to try.

ee02217 commented 2 years ago

And version 1.6.2 without humidity for za1.

droserHA commented 2 years ago

And version 1.6.2 without humidity for za1.

Logger: homeassistant.components.climate Source: custom_components/miheater/climate.py:204 Integration: Климат (documentation, issues) First occurred: 14:12:16 (1 occurrences) Last logged: 14:12:16

Error while setting up miheater platform for climate Traceback (most recent call last): File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 249, in _async_setup_platform await asyncio.shield(task) File "/usr/local/lib/python3.9/concurrent/futures/thread.py", line 52, in run result = self.fn(*self.args, **self.kwargs) File "/config/custom_components/miheater/climate.py", line 84, in setup_platform miHeater = MiHeater(device, name, model, unique_id, hass) File "/config/custom_components/miheater/climate.py", line 122, in init self.getAttrData() File "/config/custom_components/miheater/climate.py", line 204, in getAttrData data['power'] = power[0]["value"] KeyError: 'value'

ee02217 commented 2 years ago

It seems it's not even getting the Power status from your heater. This means that these codes are not correct: power=self._device.raw_command('get_properties', [{"siid":2,"piid":1}])

This is the hard part :p

droserHA commented 2 years ago

My JF1 has humidity

From: sygys @.> Sent: Wednesday, September 29, 2021 1:53 PM To: ee02217/homeassistant-mi-heater @.> Cc: droserHA @.>; Mention @.> Subject: Re: [ee02217/homeassistant-mi-heater] Not work on zhimi.heater.za1 (#6)

maybe this:

@property
def current_humidity(self):
    """Return the current humidity."""
    return self._state['humidity']

can be removed completely if you dont use it right? But you had it in the code for when some one uses the zb1 offcourse

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ee02217/homeassistant-mi-heater/issues/6#issuecomment-930066066 , or unsubscribe https://github.com/notifications/unsubscribe-auth/ATKGMY3VEKVYIO4IPJ5NRLTUELVX7ANCNFSM4VICP2NQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub .

droserHA commented 2 years ago

It seems it's not even getting the Power status from your heater. This means that these codes are not correct: power=self._device.raw_command('get_properties', [{"siid":2,"piid":1}])

This is the hard part :p

in za1 there is temperature and humidity!!!

sygys commented 2 years ago

thats strange looking at the xiaomi page the siid and piid seems to be the values i put in. I recommend using node red to fire these commands at the heater. that way you get a payload back which tells you what goes wrong. My knowledge isnt good enough to help you further

sygys commented 2 years ago

maybe the za1 also needs this: "did":DEVICE_ID??

ee02217 commented 2 years ago

One way you can test, is by installing python-miio (https://python-miio.readthedocs.io/en/latest/discovery.html#installation) and trying the commands like this: miiocli device --ip 192.168.1.XXX --token ZZZZZZZZZZZZZZZZZZZZZZZZ raw_command get_properties '[{"piid":1,"siid":2}]'

This will allow you to validate the necessary values for using in the code :)

droserHA commented 2 years ago

just send me climate.py and I will test))))

ee02217 commented 2 years ago

It's updated on new version. It's a beta release.

image

droserHA commented 2 years ago

It's updated on new version. It's a beta release.

image

I tested. did not work(((