Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zhimi.heater.mc2 not fully supported #880

Closed
ee02217 opened this issue Dec 3, 2020 · 19 comments
Closed

zhimi.heater.mc2 not fully supported #880

ee02217 opened this issue Dec 3, 2020 · 19 comments

Comments

@ee02217
Copy link

ee02217 commented Dec 3, 2020

Describe the bug
zhimi.heater.mc2 is not fully supported.
Info command works correctly providing the below output:
$ miiocli heater --ip 192.168.86.94 --token XXXXXXXXXXXXXX info Model: zhimi.heater.mc2 Hardware version: esp32 Firmware version: 2.0.8
However, any other command results in error:
$ miiocli heater --ip 192.168.86.94 --token XXXXXXXXXXXXXX on Powering on Error: {'code': -5001, 'message': 'command error'}

Version information (please complete the following information):

  • OS: Ubuntu 20.04
  • python-miio: miiocli, version 0.5.4

Device information:

  • Model: zhimi.heater.mc2
  • Hardware version: esp32
  • Firmware version: 2.0.8

To Reproduce
Steps to reproduce the behavior:

  1. run command: miiocli heater --ip 192.168.86.94 --token XXXXXXXXXXXXXX on

Expected behavior
Heater should turn on.

Console output
Powering on Error: {'code': -5001, 'message': 'command error'}

@ee02217 ee02217 added the bug label Dec 3, 2020
@ee02217
Copy link
Author

ee02217 commented Dec 3, 2020

It seems this should not be a bug, but a new feature, but I dont seem to be able to change that.

@syssi
Copy link
Collaborator

syssi commented Dec 3, 2020

Miot specs: https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:heater:0000A01A:zhimi-mc2:1

Service: siid 1: (Device Information): 4 props, 0 actions
  * Property piid: 1 (Device Manufacturer): (string, unit: None) (acc: ['read'], value-list: [], value-range: None)
  * Property piid: 2 (Device Model): (string, unit: None) (acc: ['read'], value-list: [], value-range: None)
  * Property piid: 3 (Device Serial Number): (string, unit: None) (acc: ['read'], value-list: [], value-range: None)
  * Property piid: 4 (Current Firmware Version): (string, unit: None) (acc: ['read'], value-list: [], value-range: None)
Service: siid 2: (Heater): 3 props, 0 actions
  * Property piid: 1 (Switch Status): (bool, unit: None) (acc: ['read', 'write', 'notify'], value-list: [], value-range: None)
  * Property piid: 2 (Device Fault): (uint8, unit: none) (acc: ['read', 'notify'], value-list: [], value-range: [0, 255, 1])
  * Property piid: 5 (Target Temperature): (float, unit: celsius) (acc: ['read', 'write', 'notify'], value-list: [], value-range: [18, 28, 1])
Service: siid 3: (Countdown): 1 props, 0 actions
  * Property piid: 1 (Countdown Time): (uint32, unit: hours) (acc: ['read', 'write', 'notify'], value-list: [], value-range: [0, 12, 1])
Service: siid 4: (Environment): 1 props, 0 actions
  * Property piid: 7 (Temperature): (float, unit: celsius) (acc: ['read', 'notify'], value-list: [], value-range: [-30, 100, 0.1])
Service: siid 5: (Physical Control Locked): 1 props, 0 actions
  * Property piid: 1 (Physical Control Locked): (bool, unit: None) (acc: ['read', 'write', 'notify'], value-list: [], value-range: None)
Service: siid 6: (Alarm): 1 props, 0 actions
  * Property piid: 1 (Alarm): (bool, unit: None) (acc: ['read', 'write', 'notify'], value-list: [], value-range: None)
Service: siid 7: (Indicator Light): 1 props, 0 actions
  * Property piid: 3 (Brightness): (uint8, unit: percentage) (acc: ['read', 'write', 'notify'], value-list: [], value-range: [0, 1, 1])
Service: siid 8: (私有服务): 4 props, 0 actions
  * Property piid: 1 (按键点击): (uint8, unit: none) (acc: ['notify'], value-list: [], value-range: [0, 4, 1])
  * Property piid: 8 (恒温功能使能): (bool, unit: none) (acc: ['read', 'notify', 'write'], value-list: [], value-range: None)
  * Property piid: 9 (用户使用时长): (uint32, unit: None) (acc: ['read', 'notify'], value-list: [], value-range: [0, 2147483647, 1])
  * Property piid: 10 (country-code): (int32, unit: none) (acc: ['read', 'notify'], value-list: [{'value': 0, 'description': '未知'}, {'value': 1, 'description': 'US, 美国'}, {'value': 82, 'description': 'KR,韩国'}, {'value': 44, 'description': 'EU,欧洲'}, {'value': 81, 'description': 'JP,日本'}, {'value': 7, 'description': 'RU,俄罗斯'}, {'value': 86, 'description': 'CN,中国'}, {'value': 852, 'description': '中国香港'}, {'value': 886, 'description': '中国台湾'}, {'value': 33, 'description': 'FR,法国'}], value-range: None)
  * Event eiid 1 (高温异常): (args: [])
  * Event eiid 2 (NTC 异常): (args: [])
  * Event eiid 3 (状态异常上报。 目前没必要实现): (args: [])
  * Event eiid 4 (eeprom 异常): (args: [])
  * Event eiid 5 (显示通信异常): (args: [])
  * Event eiid 6 (童锁状态下1s内多次连续触发次数,实现方式暂时存疑惑): (args: [])

@ee02217
Copy link
Author

ee02217 commented Dec 3, 2020

I can confirm that the below commands work:

###READS###
Get Target Temperature
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":5,"siid":2}]'

Get Switch Status
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":1,"siid":2}]'

Get fault
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":2,"siid":2}]'

Get Mode - NOT WORKING
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":6,"siid":2}]'

Get Environment
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":1,"siid":3}]'

Get Temperature
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":7,"siid":4}]'

Get Physical Control Locked stauts
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":1,"siid":5}]'

Get Alarm stauts
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command get_properties '[{"piid":1,"siid":6}]'

###WRITES###
Switch Status
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":True,"siid":2,"piid":1}]'

Countdown
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":36000,"siid":3,"piid":1}]'

Switch Status
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":True,"siid":2,"piid":1}]'

Target Temperature
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":22,"siid":2,"piid":5}]'

Mode
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":1,"siid":2,"piid":6}]'

Physical Control Locked
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":True,"siid":5,"piid":1}]'

Alarm
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":True,"siid":6,"piid":1}]'

Indicator Light
miiocli device --ip <<IP>> --token <<TOKEN>> raw_command set_properties '[{"value":0,"siid":7,"piid":3}]'

The problem is that it only works with raw_commands, not with heater device commands.

@syssi
Copy link
Collaborator

syssi commented Dec 3, 2020

The problem is that it only works with raw_commands, not with heater device commands.

This is fine because the heater implementation doesn't cover your device right now. I assume there will be a heatermc2 parameter as soon your device is added.

@ee02217
Copy link
Author

ee02217 commented Dec 3, 2020

Great! Thank you for the feedback!
I'll be available for any testing, if needed :)

@rezmus
Copy link

rezmus commented Dec 4, 2020

already several models using miotspec (ma2, ma3, mc2, na1, nb1, za2, zb1).

@ee02217
Copy link
Author

ee02217 commented Dec 4, 2020

I'm not a developer, but I created a HeaterMiot.py that I tested and it works. If someone can create the pull request it would be great.

Apart from this new file, only a small edit to init to include this new class.

`
import enum
import logging
from typing import Any, Dict, Optional
from datetime import timedelta

import click

#from .airfilter_util import FilterType, FilterTypeUtil
from .click_common import EnumType, command, format_output
from .exceptions import DeviceException
from .miot_device import MiotDevice

_LOGGER = logging.getLogger(name)
_MAPPING = {
"power": {"siid": 2, "piid": 1},
"target_temperature": {"siid": 2, "piid": 5},
"countdown_time": {"siid": 3, "piid": 1},
"temperature": {"siid": 4, "piid": 7},
"user_usage_time": {"siid": 8, "piid": 9},
}

class LedBrightness(enum.Enum):
Bright = 0
Dim = 1
Off = 2

class HeaterMiotException(DeviceException):
pass

class HeaterMiotStatus:
"""Container for status reports from the heater."""

def __init__(self, data: Dict[str, Any]) -> None:
    #self.filter_type_util = FilterTypeUtil()
    self.data = data

@property
def is_on(self) -> bool:
    """Return True if device is on."""
    return self.data["power"]

@property
def power(self) -> str:
    """Power state."""
    return "on" if self.is_on else "off"


@property
def temperature(self) -> Optional[float]:
    """Current temperature, if available."""
    if self.data["temperature"] is not None:
        return round(self.data["temperature"], 1)
    return None

@property
def target_temperature(self) -> Optional[float]:
    """Target temperature, if available."""
    if self.data["target_temperature"] is not None:
        return round(self.data["target_temperature"], 1)
    return None



@property
def user_usage_time(self) -> int:
    """How long the device has been active in seconds."""
    #return self.data["user_usage_time"]
    return timedelta(seconds=self.data["user_usage_time"])

@property
def countdown_time(self) -> int:
    """The countdown to shutdown."""
    return self.data["countdown_time"]



def __repr__(self) -> str:
    s = (
        "<HeaterMiotStatus power=%s, "
        "temperature=%s, "
        "target_temperature=%s, "
        "countdown_time=%s, "
        "user_usage_time=%s>"
        % (
            self.power,
            self.temperature,
            self.target_temperature,
            self.countdown_time,
            self.user_usage_time,
        )
    )
    return s

class HeaterMiot(MiotDevice):
"""Main class representing the air purifier which uses MIoT protocol."""

def __init__(
    self,
    ip: str = None,
    token: str = None,
    start_id: int = 0,
    debug: int = 0,
    lazy_discover: bool = True,
) -> None:
    super().__init__(_MAPPING, ip, token, start_id, debug, lazy_discover)

@command(
    default_output=format_output(
        "",
        "Power: {result.power}\n"
        "Temperature: {result.temperature} °C\n"
        "Target Temperature: {result.target_temperature} °C\n"
        "Countdown Timer: {result.countdown_time} h \n"
        "Usage Time: {result.user_usage_time} s\n",
    )
)
def status(self) -> HeaterMiotStatus:
    """Retrieve properties."""

    return HeaterMiotStatus(
        {
            prop["did"]: prop["value"] if prop["code"] == 0 else None
            for prop in self.get_properties_for_mapping()
        }
    )

@command(default_output=format_output("Powering on"))
def on(self):
    """Power on."""
    return self.set_property("power", True)

@command(default_output=format_output("Powering off"))
def off(self):
    """Power off."""
    return self.set_property("power", False)

@command(
    click.argument("target_temperature", type=int),
    default_output=format_output("Setting target temperature to '{target_temperature}'"),
)
def target_temperature(self, target_temperature: int):
    """Set target_temperature ."""
    if target_temperature < 18 or target_temperature > 38:
        raise HeaterMiotException("Invalid temperature: %s" % target_temperature)
    return self.set_property("target_temperature", target_temperature)

@command(
    click.argument("brightness", type=EnumType(LedBrightness)),
    default_output=format_output("Setting LED brightness to {brightness}"),
)
def set_led_brightness(self, brightness: LedBrightness):
    """Set led brightness."""
    return self.set_property("led_brightness", brightness.value)

`

@myrayearth
Copy link

I'm not a developer, but I created a HeaterMiot.py that I tested and it works. If someone can create the pull request it would be great.

Thank you for your analysis.
Successfully controlled raw_command through Miiocli.

If you want to use this file (HeaterMiot.py) on your home Assistant, see the
Can you tell me what to do?
I am using Home Assistant based on Docker.

@ee02217
Copy link
Author

ee02217 commented Dec 5, 2020

If you want to use this file (HeaterMiot.py) on your home Assistant, see the
Can you tell me what to do?

I can’t really understand if you missed some text there.
I would love to integrate this in home assistant , but I have no idea how... :(

@mouth4war
Copy link
Contributor

mouth4war commented Dec 5, 2020 via email

@ee02217
Copy link
Author

ee02217 commented Dec 5, 2020

It doesn’t support this device. And people are saying it doesn’t work anymore :(

@mouth4war
Copy link
Contributor

mouth4war commented Dec 5, 2020 via email

@failover88
Copy link

failover88 commented Dec 20, 2020

Through,

miiocli device --ip 192.168.1.82 --token 32CHARTOKEN raw_command get_properties '[{"piid":5,"siid":2}]'

I got,

Running command raw_command
ERROR:miio.miioprotocol:Got error when receiving: timed out

I have run other raw_command's like several users here, but same result. The device was added to local network through Mi Home app.

The device token is the right one, got while it was in AP mode through Packet Sender.

Specs:

  • python-miio version: 0.5.4
  • zhmi.heater.mc2 version: 2.0.8.0003
  • Python: 3.9.1
  • host OS: debian 8.11

@rezmus
Copy link

rezmus commented Dec 20, 2020

token changes each time your device connects to wifi (is paired). token from ap mode is useless after bind.

@failover88
Copy link

token changes each time your device connects to wifi (is paired). token from ap mode is useless after bind.

What method do you recommend to get that token after paired?

@syssi
Copy link
Collaborator

syssi commented Dec 20, 2020

There are some xiaomi cloud clients which can retrieve all tokens of your mihome account from the cloud.

f.e. https://github.com/PiotrMachowski/Xiaomi-cloud-tokens-extractor

@bafonins
Copy link
Contributor

Added support for this heater in #895

@bafonins
Copy link
Contributor

bafonins commented Jan 6, 2021

@rytilahti Can we close this as support for this device was merged in #895 ?

@syssi
Copy link
Collaborator

syssi commented Jan 6, 2021

Yes. Let's close this issue. The device is supported now. Thanks!

@syssi syssi closed this as completed Jan 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants