Midea/Inventor (and other brand) Air Condition API

Topics (not sure which fora)
when not sure where to post, post here and mods will move it to right forum.

Moderators: leecollings, remb0

User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

Alain wrote: Friday 08 May 2020 3:37 I have been messing around for days, trying to integrate different plugins, combinations of versions, modifying the Daikin one, etc but I can't get anything to work. Either Domoticz doesn't see them and they don't show up in the hardware list, or I'm getting errors in all sorts.

There are lots of these units out there and I can hardly imagine that there is not a soul that doesn't want to control their airco from Domoticz.
That will not work, the closest thing you can get for a base plugin is either the openhab version, or the latest HomeAssistant plugin.
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

I've tried the HA one, but can't get that to run either. I'm at my wit's end.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

Alain wrote: Saturday 09 May 2020 11:29 I've tried the HA one, but can't get that to run either. I'm at my wit's end.
With a simple copy&paste it will not work for sure. One have to modify and adapt that source to fit into domoticz's python plugin framework. I1ve played with the midea python library under OpenHAB and I was able to switch on and off the AC, so I'm pretty sure the code itself is at least able to communicate with the Amazon Cloud.
rafaelgc
Posts: 4
Joined: Thursday 04 June 2020 20:58
Target OS: -
Domoticz version:
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by rafaelgc »

Hi all.

Concerning the module itself, is it a simple USB/WIFI dongle or does it contain something else? I have a compatible AC machine and I would like to control it through domoticz. But I don´t want to spend 60€ in a simple wifi dongle.

What can you tell me about the dongle?

Thanks
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

rafaelgc wrote: Thursday 04 June 2020 21:28 Hi all.

Concerning the module itself, is it a simple USB/WIFI dongle or does it contain something else? I have a compatible AC machine and I would like to control it through domoticz. But I don´t want to spend 60€ in a simple wifi dongle.

What can you tell me about the dongle?

Thanks
Donno, never opened it, but I suspect it's more than just a simple usb wifi dongle :)
rafaelgc
Posts: 4
Joined: Thursday 04 June 2020 20:58
Target OS: -
Domoticz version:
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by rafaelgc »

Thanks, Louis.

I agree with you, i guess it's more than a simple dongle. Maybe someone can open it to see whats inside, or just attach it to a computer and see what happens (possible hardware id...)

Maybe it contains some readable chip... and maybe we can read it... and maybe the readed memory can point us in the correct direction...

...yes, a lot of "maybes"... :lol:
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

It has some sort of address, because you need to scan a QR-code to connect it to the Nethome app. I exchanged a complete airco unit once because the one I had, had a defect. I put the dongle from the old unit in the new one and didn't have to set anything up. In the Nethome app it was just as if the old unit was still there.

In the meantime, I have still not been able to get the Midea python plugin (written for Home Assistant) to work in Domoticz. Obviously python is not my strongest point. :cry:
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
rafaelgc
Posts: 4
Joined: Thursday 04 June 2020 20:58
Target OS: -
Domoticz version:
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by rafaelgc »

I saw those qrcodes, but no idea of the content written on them.

Regarding your information, the wifi/usb module itself contains some unique information that allows the app to associate the module to a some particular user account. That may be done with a simple mac-address... or maybe something else.

As different brands of AC shares the same system and can be managed with the same app, we can assume that all of them will work with the same set of "commands".

All those commands can be stored in three places: the app server in AWS, the app on phone or the module itself. I assume that the server only manages the association client-module (because the app works even if the internet connection is lost).

It would be interesting if someone could identify the kind of hardware we are talking about.
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

To save you time, this topic contains virtually everything :)

https://community.home-assistant.io/t/a ... -a-c/18742
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

Thanks LouiS22.

I read the HA thread and found some useful info there. i installed https://github.com/mac-zhou/midea-msmart and it works for me in a terminal window. I can get the status of an airco and I can write values to it and see it change in the Nethome Plus app. I'm going to try and get it working in the Domoticz framework now.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
rafaelgc
Posts: 4
Joined: Thursday 04 June 2020 20:58
Target OS: -
Domoticz version:
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by rafaelgc »

LouiS22 wrote: Tuesday 09 June 2020 13:44 To save you time, this topic contains virtually everything :)

https://community.home-assistant.io/t/a ... -a-c/18742
Really appreciate your link. I had a very long reading session. My conclusion is not to buy the wifi module. I think IR bridge is the cheaper (20€ for a broadlink) and more convinient approach to this kind of control. Python is not my best skill and the work to handle it is already done in domoticz.

Thanks.
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

rafaelgc wrote: Monday 15 June 2020 15:35
LouiS22 wrote: Tuesday 09 June 2020 13:44 To save you time, this topic contains virtually everything :)

https://community.home-assistant.io/t/a ... -a-c/18742
Really appreciate your link. I had a very long reading session. My conclusion is not to buy the wifi module. I think IR bridge is the cheaper (20€ for a broadlink) and more convinient approach to this kind of control. Python is not my best skill and the work to handle it is already done in domoticz.

Thanks.
And you'll surely have serious problem implementing the IR codes onto that cheap chinese stuff. A/C remotes tend to send long and complex IR codes, not just a simple on and off command. That's where those IR blasters have problems. My advice: stick with the usb stick. :)
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

I've played a bit with the local msmart library provided by the guy @ Home Assistant forum.

My Midea device is supported, it was recognized instantly, and with the provided example.py I was able to turn on the AC and set stuff. :)
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

Yeah, me too. I am still busy getting it to work in Domoticz. I have it setup so that I have all the devices visible, but I haven't been able to get the current status showing up in Domoticz, or control the airco unit from it. I'm getting close, however. Sometimes I'll change something and then get errors in the log, but that usually clears something up for me which means I'm getting closer to the solution. Once I get it running, the next step would be to try and get it as a complete package in Github so I can share it.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

Oh, and I'm using Qlima airco units and can control them too, using the midea-msmart library (which is what I'm basing my Domoticz plugin on).
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

Now able to switch units on and off, change the mode and set the temperature. I can also change the fan speed. Getting close now.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

Done!

For some reason some of the commands don't work on a Qlima airco, so I didn't spend any time implementing and fine tuning them. The library I used was originally for a Midea unit. I found it here: https://github.com/mac-zhou/midea-msmart
My adapted code:

Code: Select all

"""
<plugin key="Qlima" name="Qlima Wifi airconditioner" author="Alain" version="1.0.0">
    <description>
        <h2>Qlima Wifi AС</h2><br/>
        <h3>Features</h3>
        <ul style="list-style-type:square">
            <li>On/Off</li>
            <li>HVAC Mode: Auto, Cool, Heat, Fan, Dry</li>
            <li>Fan speed: Low, Medium, High, Auto</li>
            <li>Swing: Off, Vertical, Horizontal, Both</li>
            <li>Set temp of (C) from 17 to 30</li>
            <li>Presets: Normal, ECO, Turbo</li>
            <li>Display: On/Off</li>
        </ul>
    </description>
    <params>
        <param field="Address" label="IP Address" width="200px" required="true" default="192.168.0.61"/>
        <param field="Mode3" label="Unit id" width="300px" required="true" default="18691697687115"/>
        <param field="Mode1" label="Update interval (sec):" width="75px">
            <options>
                <option label="30" value="3" />
                <option label="60" value="6" default="true" />
                <option label="90" value="9" />
                <option label="120" value="12" />
            </options>
        </param>
        <param field="Mode2" label="Debug" width="75px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal"  default="true" />
            </options>
        </param>
    </params>
</plugin>
"""
import Domoticz
import sys

sys.path.append('/usr/lib/python3/dist-packages')

from msmart.device import air_conditioning_device as ac
from msmart.device import device as midea_device


class BasePlugin:
    
    enabled = True
    run_counter = 0


    def __init__(self):
        return

    def onStart(self):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()

# get AC info
        device.refresh() 
        
        
        if Parameters["Mode2"] == "Debug":
            Domoticz.Debugging(1)
            DumpConfigToLog()

        if len(Devices) == 0:
            Domoticz.Device(Name="Power", Unit=1, Image=9, TypeName="Switch", Used=1).Create()
            Domoticz.Device(Name="Temp Inside", Unit=2, TypeName="Temperature", Used=1).Create()
            Domoticz.Device(Name="Temp Outside", Unit=3, TypeName="Temperature", Used=1).Create()
            Domoticz.Device(Name="Setpoint", Unit=4, Type=242, Subtype=1, Image=16, Used=1).Create()

            Options = {"LevelActions": "||||||",
                       "LevelNames": "|Auto|Heat|Cool|Dry|Fan",
                       "LevelOffHidden": "true",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Mode", Unit=5, TypeName="Selector Switch", Image=16, Options=Options, Used=1).Create()

            Options = {"LevelActions": "|||||",
                       "LevelNames": "|High|Medium|Low|Auto",
                       "LevelOffHidden": "true",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Fan Rate", Unit=6, TypeName="Selector Switch", Image=7, Options=Options,
                            Used=1).Create()

            Options = {"LevelActions": "|||",
                       "LevelNames": "Off|Vertical|Horizontal|Both",
                       "LevelOffHidden": "false",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Swing", Unit=7, TypeName="Selector Switch", Image=7, Options=Options, Used=1).Create()

            
            Domoticz.Device(Name="Turbo", Unit=8, Image=9, TypeName="Switch", Used=1).Create()

            Domoticz.Device(Name="Eco", Unit=9, Image=9, TypeName="Switch", Used=1).Create()

            Domoticz.Debug("Qlima Wifi Airco device created.")

        DumpConfigToLog()

        Domoticz.Heartbeat(10)

        self.DataUpdate()

    def onStop(self):
        Domoticz.Debug("onStop called")
        return True

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")

    def onMessage(self, Connection, Data):
        Domoticz.Debug("onMessage called")

    def onCommand(self, Unit, Command, Level, Hue):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()
      
        Domoticz.Debug(
            "Command received U=" + str(Unit) + " C=" + str(Command) + " L= " + str(Level) + " H= " + str(Hue))

        try:
            device.refresh()
        except:
            Domoticz.Error("AC (" + Parameters["Address"] + ") unavailable OnCommand")
            return

        if (Unit == 1):
            if (Command == "On"):
                device.power_state = True
                device.apply()
                Devices[1].Update(nValue=1, sValue="On")

            else:
                device.power_state = False
                device.apply()
                Devices[1].Update(nValue=0, sValue="Off")



        if (Unit == 4):
            if Level > 30:
                Level = 30
            elif Level < 17:
                Level = 17

            device.target_temperature = int(Level)
            device.apply()
            Devices[4].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 5):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '10'):
                device.operational_mode = ac.operational_mode_enum.auto
                device.apply()
            elif (str(Level) == '20'):
                device.operational_mode = ac.operational_mode_enum.heat
                device.apply()
            elif (str(Level) == '30'):
                device.operational_mode = ac.operational_mode_enum.cool
                device.apply()
            elif (str(Level) == '40'):
                device.operational_mode = ac.operational_mode_enum.dry
                device.apply()
            else:
                device.operational_mode = ac.operational_mode_enum.fan_only
                device.apply()
            
            Devices[5].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 6):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '10'):
                device.fan_speed = ac.fan_speed_enum.High
                device.apply()
            elif (str(Level) == '20'):
                device.fan_speed = ac.fan_speed_enum.Medium
                device.apply()
            elif (str(Level) == '30'):
                device.fan_speed = ac.fan_speed_enum.Low
                device.apply()
            
            else:
                device.fan_speed = ac.fan_speed_enum.Auto
                device.apply()
            
            Devices[6].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 7):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '0'):
                device.swing_mode = ac.swing_mode_enum.Off
                device.apply()
            elif (str(Level) == '10'):
                device.swing_mode = ac.swing_mode_enum.Vertical
                device.apply()
            elif (str(Level) == '20'):
                device.swing_mode = ac.swing_mode_enum.Horizontal
                device.apply()
            
            else:
                device.swing_mode = ac.swing_mode_enum.Both
                device.apply()
            
            Devices[7].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 8):
            if (Command == "On"):
                device.eco_mode = False
                device.turbo_mode = True
                device.apply()
                Devices[8].Update(nValue=1, sValue="On")

            else:
                device.turbo_mode = False
                device.apply()
                Devices[8].Update(nValue=0, sValue="Off")



        if (Unit == 9):
            if (Command == "On"):
                device.turbo_mode = False
                device.eco_mode = True
                device.apply()
                Devices[9].Update(nValue=1, sValue="On")

            else:
                device.eco_mode = False
                device.apply()
                Devices[9].Update(nValue=0, sValue="Off")



    def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
        Domoticz.Debug("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(
            Priority) + "," + Sound + "," + ImageFile)


    def onDisconnect(self, Connection):
        Domoticz.Debug("onDisconnect called")


    def onHeartbeat(self):
        Domoticz.Debug("onHeartbeat called")
        self.run_counter = self.run_counter - 1
        if self.run_counter <= 0:
            Domoticz.Debug("Poll unit")
            self.run_counter = int(Parameters["Mode1"])
            self.DataUpdate()
        else:
            Domoticz.Debug("Polling unit in " + str(self.run_counter) + " heartbeats.")


    def DataUpdate(self):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()
        
        try:
            device.refresh()
            Devices[1].Update(nValue=device.power_state, sValue=str(device.power_state))
            Devices[2].Update(nValue=0, sValue=str(device.indoor_temperature))
            Devices[3].Update(nValue=0, sValue=str(device.outdoor_temperature))
            Devices[4].Update(nValue=device.power_state, sValue=str(device.target_temperature))
            
            
            Devices[5].Update(nValue=device.power_state, sValue=str(device.operational_mode))
            if str(device.operational_mode) == 'operational_mode_enum.auto':
                Level = '10'
            elif str(device.operational_mode) == 'operational_mode_enum.heat':
                Level = '20'
            elif str(device.operational_mode) == 'operational_mode_enum.cool':
                Level = '30'
            elif str(device.operational_mode) == 'operational_mode_enum.dry':
                Level = '40'
            else:
                Level = '50'
            Devices[5].Update(nValue=device.power_state, sValue=str(Level))
            
            
            
            Devices[6].Update(nValue=device.power_state, sValue=str(device.fan_speed))
            if str(device.fan_speed) == 'fan_speed_enum.High':
                Level = '10'
            elif str(device.fan_speed) == 'fan_speed_enum.Medium':
                Level = '20'
            elif str(device.fan_speed) == 'fan_speed_enum.Low':
                Level = '30'
            else:
                Level = '40'
            Devices[6].Update(nValue=device.power_state, sValue=str(Level))
            
                        
            Devices[7].Update(nValue=device.power_state, sValue=str(device.swing_mode))
            if str(device.swing_mode) == 'swing_mode_enum.Vertical':
                Level = '10'
            elif str(device.swing_mode) == 'swing_mode_enum.Horizontal':
                Level = '20'
            elif str(device.swing_mode) == 'swing_mode_enum.Both':
                Level = '30'
            else:
                Level = '0'
            Devices[7].Update(nValue=device.power_state, sValue=str(Level))
                     
            
            Devices[8].Update(nValue=device.turbo_mode, sValue=str(device.turbo_mode))
            Devices[9].Update(nValue=device.eco_mode, sValue=str(device.eco_mode))

        except:
            Domoticz.Error("Qlima (" + Parameters["Address"] + ") unavailable")
            return        


global _plugin
_plugin = BasePlugin()


def onStart():
    global _plugin
    _plugin.onStart()


def onStop():
    global _plugin
    _plugin.onStop()


def onConnect(Connection, Status, Description):
    global _plugin
    _plugin.onConnect(Connection, Status, Description)


def onMessage(Connection, Data):
    global _plugin
    _plugin.onMessage(Connection, Data)


def onCommand(Unit, Command, Level, Hue):
    global _plugin
    _plugin.onCommand(Unit, Command, Level, Hue)


def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
    global _plugin
    _plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)


def onDisconnect(Connection):
    global _plugin
    _plugin.onDisconnect(Connection)


def onHeartbeat():
    global _plugin
    _plugin.onHeartbeat()

    # Generic helper functions


def DumpConfigToLog():
    for x in Parameters:
        if Parameters[x] != "":
            Domoticz.Debug("'" + x + "':'" + str(Parameters[x]) + "'")
    Domoticz.Debug("Device count: " + str(len(Devices)))
    for x in Devices:
        Domoticz.Debug("Device:           " + str(x) + " - " + str(Devices[x]))
        Domoticz.Debug("Device ID:       '" + str(Devices[x].ID) + "'")
        Domoticz.Debug("Device Name:     '" + Devices[x].Name + "'")
        Domoticz.Debug("Device nValue:    " + str(Devices[x].nValue))
        Domoticz.Debug("Device sValue:   '" + Devices[x].sValue + "'")
        Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
    return

It may not been cleanest and most efficient code, but it works for me. It's my first time programming in Python and also getting to know how the Domoticz framework fits in with Python. Don't ask too many questions about the code, but feel free to adapt/adopt/use/share it.
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

Alain wrote: Saturday 04 July 2020 4:56 Done!

For some reason some of the commands don't work on a Qlima airco, so I didn't spend any time implementing and fine tuning them. The library I used was originally for a Midea unit. I found it here: https://github.com/mac-zhou/midea-msmart
My adapted code:

Code: Select all

"""
<plugin key="Qlima" name="Qlima Wifi airconditioner" author="Alain" version="1.0.0">
    <description>
        <h2>Qlima Wifi AС</h2><br/>
        <h3>Features</h3>
        <ul style="list-style-type:square">
            <li>On/Off</li>
            <li>HVAC Mode: Auto, Cool, Heat, Fan, Dry</li>
            <li>Fan speed: Low, Medium, High, Auto</li>
            <li>Swing: Off, Vertical, Horizontal, Both</li>
            <li>Set temp of (C) from 17 to 30</li>
            <li>Presets: Normal, ECO, Turbo</li>
            <li>Display: On/Off</li>
        </ul>
    </description>
    <params>
        <param field="Address" label="IP Address" width="200px" required="true" default="192.168.0.61"/>
        <param field="Mode3" label="Unit id" width="300px" required="true" default="18691697687115"/>
        <param field="Mode1" label="Update interval (sec):" width="75px">
            <options>
                <option label="30" value="3" />
                <option label="60" value="6" default="true" />
                <option label="90" value="9" />
                <option label="120" value="12" />
            </options>
        </param>
        <param field="Mode2" label="Debug" width="75px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal"  default="true" />
            </options>
        </param>
    </params>
</plugin>
"""
import Domoticz
import sys

sys.path.append('/usr/lib/python3/dist-packages')

from msmart.device import air_conditioning_device as ac
from msmart.device import device as midea_device


class BasePlugin:
    
    enabled = True
    run_counter = 0


    def __init__(self):
        return

    def onStart(self):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()

# get AC info
        device.refresh() 
        
        
        if Parameters["Mode2"] == "Debug":
            Domoticz.Debugging(1)
            DumpConfigToLog()

        if len(Devices) == 0:
            Domoticz.Device(Name="Power", Unit=1, Image=9, TypeName="Switch", Used=1).Create()
            Domoticz.Device(Name="Temp Inside", Unit=2, TypeName="Temperature", Used=1).Create()
            Domoticz.Device(Name="Temp Outside", Unit=3, TypeName="Temperature", Used=1).Create()
            Domoticz.Device(Name="Setpoint", Unit=4, Type=242, Subtype=1, Image=16, Used=1).Create()

            Options = {"LevelActions": "||||||",
                       "LevelNames": "|Auto|Heat|Cool|Dry|Fan",
                       "LevelOffHidden": "true",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Mode", Unit=5, TypeName="Selector Switch", Image=16, Options=Options, Used=1).Create()

            Options = {"LevelActions": "|||||",
                       "LevelNames": "|High|Medium|Low|Auto",
                       "LevelOffHidden": "true",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Fan Rate", Unit=6, TypeName="Selector Switch", Image=7, Options=Options,
                            Used=1).Create()

            Options = {"LevelActions": "|||",
                       "LevelNames": "Off|Vertical|Horizontal|Both",
                       "LevelOffHidden": "false",
                       "SelectorStyle": "1"}

            Domoticz.Device(Name="Swing", Unit=7, TypeName="Selector Switch", Image=7, Options=Options, Used=1).Create()

            
            Domoticz.Device(Name="Turbo", Unit=8, Image=9, TypeName="Switch", Used=1).Create()

            Domoticz.Device(Name="Eco", Unit=9, Image=9, TypeName="Switch", Used=1).Create()

            Domoticz.Debug("Qlima Wifi Airco device created.")

        DumpConfigToLog()

        Domoticz.Heartbeat(10)

        self.DataUpdate()

    def onStop(self):
        Domoticz.Debug("onStop called")
        return True

    def onConnect(self, Connection, Status, Description):
        Domoticz.Debug("onConnect called")

    def onMessage(self, Connection, Data):
        Domoticz.Debug("onMessage called")

    def onCommand(self, Unit, Command, Level, Hue):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()
      
        Domoticz.Debug(
            "Command received U=" + str(Unit) + " C=" + str(Command) + " L= " + str(Level) + " H= " + str(Hue))

        try:
            device.refresh()
        except:
            Domoticz.Error("AC (" + Parameters["Address"] + ") unavailable OnCommand")
            return

        if (Unit == 1):
            if (Command == "On"):
                device.power_state = True
                device.apply()
                Devices[1].Update(nValue=1, sValue="On")

            else:
                device.power_state = False
                device.apply()
                Devices[1].Update(nValue=0, sValue="Off")



        if (Unit == 4):
            if Level > 30:
                Level = 30
            elif Level < 17:
                Level = 17

            device.target_temperature = int(Level)
            device.apply()
            Devices[4].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 5):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '10'):
                device.operational_mode = ac.operational_mode_enum.auto
                device.apply()
            elif (str(Level) == '20'):
                device.operational_mode = ac.operational_mode_enum.heat
                device.apply()
            elif (str(Level) == '30'):
                device.operational_mode = ac.operational_mode_enum.cool
                device.apply()
            elif (str(Level) == '40'):
                device.operational_mode = ac.operational_mode_enum.dry
                device.apply()
            else:
                device.operational_mode = ac.operational_mode_enum.fan_only
                device.apply()
            
            Devices[5].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 6):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '10'):
                device.fan_speed = ac.fan_speed_enum.High
                device.apply()
            elif (str(Level) == '20'):
                device.fan_speed = ac.fan_speed_enum.Medium
                device.apply()
            elif (str(Level) == '30'):
                device.fan_speed = ac.fan_speed_enum.Low
                device.apply()
            
            else:
                device.fan_speed = ac.fan_speed_enum.Auto
                device.apply()
            
            Devices[6].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 7):
            Domoticz.Error("Level is: " + str(Level))
            if (str(Level) == '0'):
                device.swing_mode = ac.swing_mode_enum.Off
                device.apply()
            elif (str(Level) == '10'):
                device.swing_mode = ac.swing_mode_enum.Vertical
                device.apply()
            elif (str(Level) == '20'):
                device.swing_mode = ac.swing_mode_enum.Horizontal
                device.apply()
            
            else:
                device.swing_mode = ac.swing_mode_enum.Both
                device.apply()
            
            Devices[7].Update(nValue=device.power_state, sValue=str(Level))



        if (Unit == 8):
            if (Command == "On"):
                device.eco_mode = False
                device.turbo_mode = True
                device.apply()
                Devices[8].Update(nValue=1, sValue="On")

            else:
                device.turbo_mode = False
                device.apply()
                Devices[8].Update(nValue=0, sValue="Off")



        if (Unit == 9):
            if (Command == "On"):
                device.turbo_mode = False
                device.eco_mode = True
                device.apply()
                Devices[9].Update(nValue=1, sValue="On")

            else:
                device.eco_mode = False
                device.apply()
                Devices[9].Update(nValue=0, sValue="Off")



    def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
        Domoticz.Debug("Notification: " + Name + "," + Subject + "," + Text + "," + Status + "," + str(
            Priority) + "," + Sound + "," + ImageFile)


    def onDisconnect(self, Connection):
        Domoticz.Debug("onDisconnect called")


    def onHeartbeat(self):
        Domoticz.Debug("onHeartbeat called")
        self.run_counter = self.run_counter - 1
        if self.run_counter <= 0:
            Domoticz.Debug("Poll unit")
            self.run_counter = int(Parameters["Mode1"])
            self.DataUpdate()
        else:
            Domoticz.Debug("Polling unit in " + str(self.run_counter) + " heartbeats.")


    def DataUpdate(self):
        device_ip = Parameters["Address"]
        device_id = Parameters["Mode3"]
        client = midea_device(device_ip, int(device_id))
        device = client.setup()
        
        try:
            device.refresh()
            Devices[1].Update(nValue=device.power_state, sValue=str(device.power_state))
            Devices[2].Update(nValue=0, sValue=str(device.indoor_temperature))
            Devices[3].Update(nValue=0, sValue=str(device.outdoor_temperature))
            Devices[4].Update(nValue=device.power_state, sValue=str(device.target_temperature))
            
            
            Devices[5].Update(nValue=device.power_state, sValue=str(device.operational_mode))
            if str(device.operational_mode) == 'operational_mode_enum.auto':
                Level = '10'
            elif str(device.operational_mode) == 'operational_mode_enum.heat':
                Level = '20'
            elif str(device.operational_mode) == 'operational_mode_enum.cool':
                Level = '30'
            elif str(device.operational_mode) == 'operational_mode_enum.dry':
                Level = '40'
            else:
                Level = '50'
            Devices[5].Update(nValue=device.power_state, sValue=str(Level))
            
            
            
            Devices[6].Update(nValue=device.power_state, sValue=str(device.fan_speed))
            if str(device.fan_speed) == 'fan_speed_enum.High':
                Level = '10'
            elif str(device.fan_speed) == 'fan_speed_enum.Medium':
                Level = '20'
            elif str(device.fan_speed) == 'fan_speed_enum.Low':
                Level = '30'
            else:
                Level = '40'
            Devices[6].Update(nValue=device.power_state, sValue=str(Level))
            
                        
            Devices[7].Update(nValue=device.power_state, sValue=str(device.swing_mode))
            if str(device.swing_mode) == 'swing_mode_enum.Vertical':
                Level = '10'
            elif str(device.swing_mode) == 'swing_mode_enum.Horizontal':
                Level = '20'
            elif str(device.swing_mode) == 'swing_mode_enum.Both':
                Level = '30'
            else:
                Level = '0'
            Devices[7].Update(nValue=device.power_state, sValue=str(Level))
                     
            
            Devices[8].Update(nValue=device.turbo_mode, sValue=str(device.turbo_mode))
            Devices[9].Update(nValue=device.eco_mode, sValue=str(device.eco_mode))

        except:
            Domoticz.Error("Qlima (" + Parameters["Address"] + ") unavailable")
            return        


global _plugin
_plugin = BasePlugin()


def onStart():
    global _plugin
    _plugin.onStart()


def onStop():
    global _plugin
    _plugin.onStop()


def onConnect(Connection, Status, Description):
    global _plugin
    _plugin.onConnect(Connection, Status, Description)


def onMessage(Connection, Data):
    global _plugin
    _plugin.onMessage(Connection, Data)


def onCommand(Unit, Command, Level, Hue):
    global _plugin
    _plugin.onCommand(Unit, Command, Level, Hue)


def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
    global _plugin
    _plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)


def onDisconnect(Connection):
    global _plugin
    _plugin.onDisconnect(Connection)


def onHeartbeat():
    global _plugin
    _plugin.onHeartbeat()

    # Generic helper functions


def DumpConfigToLog():
    for x in Parameters:
        if Parameters[x] != "":
            Domoticz.Debug("'" + x + "':'" + str(Parameters[x]) + "'")
    Domoticz.Debug("Device count: " + str(len(Devices)))
    for x in Devices:
        Domoticz.Debug("Device:           " + str(x) + " - " + str(Devices[x]))
        Domoticz.Debug("Device ID:       '" + str(Devices[x].ID) + "'")
        Domoticz.Debug("Device Name:     '" + Devices[x].Name + "'")
        Domoticz.Debug("Device nValue:    " + str(Devices[x].nValue))
        Domoticz.Debug("Device sValue:   '" + Devices[x].sValue + "'")
        Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
    return

It may not been cleanest and most efficient code, but it works for me. It's my first time programming in Python and also getting to know how the Domoticz framework fits in with Python. Don't ask too many questions about the code, but feel free to adapt/adopt/use/share it.
Wow, nice job! I've tested and it works for me (not a big surprise, as I played with the library before :D). Keep up with the work and If I my suggest, you should share the code @ github so the community can use it and upgrade it.
Alain
Posts: 166
Joined: Sunday 26 April 2020 5:27
Target OS: Linux
Domoticz version: 2022.1
Location: Netherlands
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by Alain »

I created a repository, but but it's all a bit messy. Not quite sure how to properly go about it as I have used parts from Mac_Zhou (msmart repo) and from NeoArcheron (to obtain device id).

Here's a link to my plugin: https://github.com/awalsum/Qlima
Hue | Zigbee2Mqtt | MQTT | P1 | Xiaomi | RFXCom | Modbus | Qlima | Solaredge
TP-Link | Plugwise | Thermosmart | Node-Red | Grafana | Master and 5 remote servers
User avatar
LouiS22
Posts: 433
Joined: Friday 27 February 2015 13:21
Target OS: Raspberry Pi / ODroid
Domoticz version: beta
Location: Budapest, Hungary
Contact:

Re: Midea/Inventor (and other brand) Air Condition API

Post by LouiS22 »

Alain wrote: Saturday 04 July 2020 23:50 I created a repository, but but it's all a bit messy. Not quite sure how to properly go about it as I have used parts from Mac_Zhou (msmart repo) and from NeoArcheron (to obtain device id).

Here's a link to my plugin: https://github.com/awalsum/Qlima
During your tests have you noticed an issue where the AC somehow changes its state (ie from 24 celsius and ECO to ECO OFF and 27celsius) without any interaction?
Post Reply

Who is online

Users browsing this forum: Google [Bot] and 1 guest