Plugins/MELCloud.html

From Domoticz
Jump to: navigation, search

Informations

Page (still) under construction.

Overview

Documentation about MELCloud python module.hreaded so the whole plugin system will wait.

Release Notes

v0.3 : Add Next Update information, MAC Address and Serial Number, Add Horizontal vane, Add Vertival vane, Add Room Temp

V0.2 Add synchronization between MELCloud and Domoticz devices.

V0.1 Initial Release

Installation

Save the code below in a file name plugin.py

# MELCloud Plugin
# Author:     Gysmo, 2017
# Version: 0.4
#   
# Release Notes:
# v0.4 : Search devices in floors, areas and devices
# v0.3 : Add Next Update information, MAC Address  and Serial Number
#		 Add Horizontal vane
#		 Add Vertival vane
#		 Add Room Temp
# v0.2 : Add sync between Domoticz devices and MELCloud devices
#        Usefull if you use your Mitsubishi remote
# v0.1 : Initial release
"""
<plugin key="MELCloud" version="0.4" name="MELCloud plugin" author="gysmo" wikilink="http://www.domoticz.com/wiki/Plugins/MELCloud.html" externallink="http://www.melcloud.com">
    <params>
        <param field="Username" label="Email" width="200px" required="true" />
        <param field="Password" label="Password" width="200px" required="true" />
        <param field="Mode6" label="Debug" width="75px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal"  default="true" />
            </options>
        </param>
    </params>
</plugin>
"""
import Domoticz
import time
import base64
import json
from urllib.parse import urlencode
from urllib.request import Request, urlopen
 
pluginState = "Not Ready"
socketOn = "FALSE"
 
melKey = ""
melDevices = []
melBuildingID = ""
 
domModeLevels = [0,1,3,7,2]
domModePic = [9,15,16,7,11]
 
domMode = {"0":0,"10":1,"20":3,"30":7,"40":2}
domNameMode = "Off|Warm|Cold|Vent|Dry"
 
# Fan Level 5 value 255
domLevelFan = {"0":1,"10":2,"20":3,"30":4,"40":255,"50":0,"60":1}
domNameFan = "Level1|Level2|Level3|Level4|Level5|Auto|Silence"
 
domLevelTemp = {"0":16,"10":17,"20":18,"30":19,"40":20,"50":21,"60":22,"70":23,"80":24,"90":25,"100":26,"110":27,"120":28,"130":29,"140":30,"150":31}
 
domLevelVaneH = {"0":1,"10":2,"20":3,"30":4,"40":5,"50":12,"60":0}
domNameVaneH = "1|2|3|4|5|Swing|Auto"
 
domLevelVaneV = {"0":1,"10":2,"20":3,"30":4,"40":5,"50":7,"60":0}
domNameVaneV = "1|2|3|4|5|Swing|Auto"
 
def melLogin():
	global melKey
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Login/ClientLogin"
	post_fields = {'AppVersion': '1.9.3.0', \
					'Language': '7', \
					'CaptchaChallange': '', \
					'CaptchaResponse': '', \
					'Persist': 'true', \
					'Email': Parameters["Username"], \
					'Password': Parameters["Password"]}
	request = Request(url, urlencode(post_fields).encode())
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	if (jsonResponse["ErrorId"] == None):
		melKey = jsonResponse["LoginData"]["ContextKey"]
		Domoticz.Debug("MELCloud Login success. Key ID:"+melKey)
	elif (jsonResponse["ErrorId"] == 1):
		Domoticz.Debug("MELCloud Login fail. Check your username and password")
	else:
		Domoticz.Debug("MELCloud Login fail. Do not knwon the reason")
	return True
 
def melListDevices():
	global melKey, melDevices, melBuildingID
	melDevice = {}
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/User/ListDevices"
	headers = {'X-MitsContextKey': melKey}
	request = Request(url,None,headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	unitModeID = 1
	unitFanID = 2
	unitThermoID = 3
	unitVaneHozID = 4
	unitVaneVerID = 5
	unitRoomTempID = 6
	unitInfosID = 7
	for building in jsonResponse:
#Search in devices
		for device in building["Structure"]["Devices"]:
			melDevice = {}
			melBuildingID = device["BuildingID"]
			melDevice['name'] = device["DeviceName"]
			melDevice['id'] = device["DeviceID"]
			melDevice['macaddr'] = device["MacAddress"]
			melDevice['sn'] = device["SerialNumber"]
			melDevice['modeID'] = unitModeID
			melDevice['fanID'] = unitFanID
			melDevice['thermoID'] = unitThermoID
			melDevice['vaneHozID'] = unitVaneHozID
			melDevice['vaneVerID'] = unitVaneVerID
			melDevice['roomTempID'] = unitRoomTempID
			melDevice['infosID'] = unitInfosID
			melDevices.append(melDevice)
			unitModeID += 7
			unitFanID += 7
			unitThermoID += 7
			unitVaneHozID += 7
			unitVaneVerID += 7
			unitRoomTempID += 7
			unitInfosID += 7
#Search in areas
		for area in building["Structure"]["Areas"]:
			for device in area["Devices"]:
				melDevice = {}
				melBuildingID = device["BuildingID"]
				melDevice['name'] = device["DeviceName"]
				melDevice['id'] = device["DeviceID"]
				melDevice['macaddr'] = device["MacAddress"]
				melDevice['sn'] = device["SerialNumber"]
				melDevice['modeID'] = unitModeID
				melDevice['fanID'] = unitFanID
				melDevice['thermoID'] = unitThermoID
				melDevice['vaneHozID'] = unitVaneHozID
				melDevice['vaneVerID'] = unitVaneVerID
				melDevice['roomTempID'] = unitRoomTempID
				melDevice['infosID'] = unitInfosID
				melDevices.append(melDevice)
				unitModeID += 7
				unitFanID += 7
				unitThermoID += 7
				unitVaneHozID += 7
				unitVaneVerID += 7
				unitRoomTempID += 7
				unitInfosID += 7
#Search in floors
		for floor in building["Structure"]["Floors"]:
			for device in floor["Devices"]:
				melDevice = {}
				melBuildingID = device["BuildingID"]
				melDevice['name'] = device["DeviceName"]
				melDevice['id'] = device["DeviceID"]
				melDevice['macaddr'] = device["MacAddress"]
				melDevice['sn'] = device["SerialNumber"]
				melDevice['modeID'] = unitModeID
				melDevice['fanID'] = unitFanID
				melDevice['thermoID'] = unitThermoID
				melDevice['vaneHozID'] = unitVaneHozID
				melDevice['vaneVerID'] = unitVaneVerID
				melDevice['roomTempID'] = unitRoomTempID
				melDevice['infosID'] = unitInfosID
				melDevices.append(melDevice)
				unitModeID += 7
				unitFanID += 7
				unitThermoID += 7
				unitVaneHozID += 7
				unitVaneVerID += 7
				unitRoomTempID += 7
				unitInfosID += 7
			for area in floor["Areas"]:
				for device in area["Devices"]:
					melDevice = {}
					melBuildingID = device["BuildingID"]
					melDevice['name'] = device["DeviceName"]
					melDevice['id'] = device["DeviceID"]
					melDevice['macaddr'] = device["MacAddress"]
					melDevice['sn'] = device["SerialNumber"]
					melDevice['modeID'] = unitModeID
					melDevice['fanID'] = unitFanID
					melDevice['thermoID'] = unitThermoID
					melDevice['vaneHozID'] = unitVaneHozID
					melDevice['vaneVerID'] = unitVaneVerID
					melDevice['roomTempID'] = unitRoomTempID
					melDevice['infosID'] = unitInfosID
					melDevices.append(melDevice)
					unitModeID += 7
					unitFanID += 7
					unitThermoID += 7
					unitVaneHozID += 7
					unitVaneVerID += 7
					unitRoomTempID += 7
					unitInfosID += 7
	return True
 
def melSetPower(melDeviceID,setPower):
	global melKey
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetAta"
	headers = {'X-MitsContextKey': melKey}
	post_fields = {'Power': setPower, \
					'DeviceID': melDeviceID, \
					'EffectiveFlags': '1', \
					'HasPendingCommand': 'true'}
	request = Request(url,urlencode(post_fields).encode(),headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	Domoticz.Debug("Next update for command power: " + jsonResponse["NextCommunication"])
	return True
 
def melSetMode(melDeviceID,setMode):
	global melKey
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetAta"
	headers = {'X-MitsContextKey': melKey}
	post_fields = {'Power': "true", \
					'OperationMode': str(setMode), \
					'DeviceID': melDeviceID, \
					'EffectiveFlags': '6', \
					'HasPendingCommand': 'true'}
	request = Request(url,urlencode(post_fields).encode(),headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	Domoticz.Debug("Next update for command mode: " + jsonResponse["NextCommunication"])
	return True
 
def melSetFan(melDeviceID,setFan):
	global melKey
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetAta"
	headers = {'X-MitsContextKey': melKey}
	post_fields = {'SetFanSpeed': setFan, \
					'DeviceID': melDeviceID, \
					'EffectiveFlags': '8', \
					'HasPendingCommand': 'true'}
	request = Request(url,urlencode(post_fields).encode(),headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	Domoticz.Debug("Next update for command fan: " + jsonResponse["NextCommunication"])
	return True
 
def melSetTemp(melDeviceID,setTemp):
	global melKey
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetAta"
	headers = {'X-MitsContextKey': melKey}
	post_fields = {'SetTemperature': setTemp, \
					'DeviceID': melDeviceID, \
					'EffectiveFlags': '4', \
					'HasPendingCommand': 'true'}
	request = Request(url,urlencode(post_fields).encode(),headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	Domoticz.Debug("Next update for command temperature: " + jsonResponse["NextCommunication"])
	return True
 
def melGetStatus(melDeviceID):
	global melKey,melBuildingID
	url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/Get?id="+str(melDeviceID)+"&buildingID="+str(melBuildingID)
	headers = {'X-MitsContextKey': melKey}
	request = Request(url,None,headers=headers)
	dataResponse = urlopen(request).read().decode()
	jsonResponse = json.loads(dataResponse)
	return jsonResponse
 
def onStart():
	global melDevices,domNameFan,domNameMode
	if Parameters["Mode6"] == "Debug":
		Domoticz.Debugging(1)
	# Define connexion to MELCloud
	melLogin()
	melListDevices()
	if (len(Devices) == 0):
		# Init Devices
		# Creation of switches
		# Image ID: 7 for fan, 15 for Temp, 16 for Mode
		Domoticz.Debug("Find " + str(len(melDevices)) + " devices in MELCloud")
		for device in melDevices:
			Domoticz.Debug("Creating device: " + device['name'] + " with melID " + str(device['id']))
			# Create Mode switch
			OptionsMode = {"LevelActions": "||||","LevelNames": domNameMode,"LevelOffHidden": "false","SelectorStyle": "1"}
			Domoticz.Device(Name=device['name']+" - Mode", Unit=device['modeID'], TypeName="Selector Switch", Image=16, Options=OptionsMode, Used=1).Create()
			# Create FAN switch
			OptionsFan = {"LevelActions": "|||||","LevelNames": domNameFan,"LevelOffHidden": "false","SelectorStyle": "1"}
			Domoticz.Device(Name=device['name']+" - Fan", Unit=device['fanID'], TypeName="Selector Switch", Image=7, Options=OptionsFan, Used=1).Create()
			# Create Temp switch
			OptionsTemp = {"LevelActions": "|||||||","LevelNames": "16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31","LevelOffHidden": "false","SelectorStyle": "1"}
			Domoticz.Device(Name=device['name']+" - Temp", Unit=device['thermoID'], TypeName="Selector Switch", Image=15, Options=OptionsTemp, Used=1).Create()
			# Create Vane Horizontal switch
			OptionsVaneH = {"LevelActions": "|||||||","LevelNames": "1|2|3|4|5|Swing|Auto","LevelOffHidden": "false","SelectorStyle": "1"}
			Domoticz.Device(Name=device['name']+" - Horiontal Vane", Unit=device['vaneHozID'], TypeName="Selector Switch", Image=7, Options=OptionsVaneH, Used=1).Create()
			# Create Vane Vertical switch
			OptionsVaneV = {"LevelActions": "|||||||||||||||","LevelNames": "1|2|3|4|5|Swing|Auto","LevelOffHidden": "false","SelectorStyle": "1"}
			Domoticz.Device(Name=device['name']+" - Vertical Vane", Unit=device['vaneVerID'], TypeName="Selector Switch", Image=7, Options=OptionsVaneV, Used=1).Create()
			# Create Room temp switch
			Domoticz.Device(Name=device['name']+" - Room Temp", Unit=device['roomTempID'], TypeName="Temperature", Used=1).Create()
			# Create Unit switch
			Domoticz.Device(Name=device['name']+" - Unit Infos", Unit=device['infosID'], TypeName="Text", Used=1).Create()
			melDeviceStatus = melGetStatus(device['id'])
			# Get values from MEL Cloud to init Domoticz switches values
			for level, fan in domLevelFan.items():
				if(fan == melDeviceStatus['SetFanSpeed']):
					setDomFan = level
			for level, temp in domLevelTemp.items():
				if(temp == melDeviceStatus['SetTemperature']):
					setDomTemp = level
			for level, vaneH in domLevelVaneH.items():
				if(vaneH == melDeviceStatus['VaneHorizontal']):
					setDomVaneH = level
			for level, vaneV in domLevelVaneV.items():
				if(vaneV == melDeviceStatus['VaneVertical']):
					setDomVaneV = level
			if(melDeviceStatus['Power'] is True):
				sValue = str(domModeLevels.index(melDeviceStatus['OperationMode'])) + "0"
				Devices[device['modeID']].Update(1,sValue,domModePic[domModeLevels.index(melDeviceStatus['OperationMode'])])
				Devices[device['fanID']].Update(1,setDomFan)
				Devices[device['thermoID']].Update(1,setDomTemp)
				Devices[device['vaneHozID']].Update(1,setDomVaneH)
				Devices[device['vaneVerID']].Update(1,setDomVaneV)
			elif(melDeviceStatus['Power'] is False):
				Devices[device['modeID']].Update(0,"0",9)
				Devices[device['fanID']].Update(0,setDomFan)
				Devices[device['thermoID']].Update(0,setDomTemp)
				Devices[device['vaneHozID']].Update(0,setDomVaneH)
				Devices[device['vaneVerID']].Update(0,setDomVaneV)
			Devices[device['roomTempID']].Update(1,str(melDeviceStatus['RoomTemperature']))
 
def onConnect(Status, Description):
	return True
 
def onMessage(Data, Status, Extra):
	return True
 
def onCommand(Unit, Command, Level, Hue):
	global melDevices
	Domoticz.Debug("onCommand called for Unit " + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
	for device in melDevices:
		for key, value in device.items():
			if(Unit == value):
				Domoticz.Debug("COUCOU " + str(Unit))
				if(key == 'modeID'):
					unitFan = Unit + 1
					unitTemp = Unit + 2
					unitVaneH = Unit + 3
					unitVaneV = Unit + 4
					if(Level == 0):
						Domoticz.Debug("Switch Off the unit "+device['name'])
						melSetPower(device['id'],"false")
						Devices[Unit].Update(0, str(Level),9)
						Devices[unitFan].Update(0,str(Devices[unitFan].sValue))
						Devices[unitTemp].Update(0,str(Devices[unitTemp].sValue))
						Devices[unitVaneH].Update(0,str(Devices[unitVaneH].sValue))
						Devices[unitVaneV].Update(0,str(Devices[unitVaneV].sValue))
					elif(Level == 10):
						Domoticz.Debug("Set to WARM the unit "+device['name'])
						melSetMode(device['id'],1)
						Devices[Unit].Update(1, str(Level),15)
					elif(Level == 20):
						Domoticz.Debug("Set to COLD the unit "+device['name'])
						melSetMode(device['id'],3)
						Devices[Unit].Update(1, str(Level),16)
					elif(Level == 30):
						Domoticz.Debug("Set to Vent the unit "+device['name'])
						melSetMode(device['id'],7)
						Devices[Unit].Update(1, str(Level),7)
					elif(Level == 40):
						Domoticz.Debug("Set to Dry the unit "+device['name'])
						melSetMode(device['id'],2)
						Devices[Unit].Update(1, str(Level),11)
					if(Level != 0):
						Devices[unitFan].Update(1,str(Devices[unitFan].sValue))
						Devices[unitTemp].Update(1,str(Devices[unitTemp].sValue))
						Devices[unitVaneH].Update(1,str(Devices[unitVaneH].sValue))
						Devices[unitVaneV].Update(1,str(Devices[unitVaneV].sValue))
				elif(key == 'fanID'):
					if(Level == 0):
						Domoticz.Debug("Change FAN  to Level 1 for "+device['name'])
						melSetFan(device['id'],"1")
					elif(Level == 10):
						Domoticz.Debug("Change FAN  to Level 2 for "+device['name'])
						melSetFan(device['id'],"2")
					elif(Level == 20):
						Domoticz.Debug("Change FAN  to Level 3 for "+device['name'])
						melSetFan(device['id'],"3")
					elif(Level == 30):
						Domoticz.Debug("Change FAN  to Level 4 for "+device['name'])
						melSetFan(device['id'],"4")
					elif(Level == 40):
						Domoticz.Debug("Change FAN  to Silence for "+device['name'])
						melSetFan(device['id'],"6")
					elif(Level == 50):
						Domoticz.Debug("Change FAN  to Auto for "+device['name'])
						melSetFan(device['id'],"0")
					Devices[Unit].Update(Devices[Unit].nValue, str(Level))
				elif(key == 'thermoID'):
					setTemp = 16
					if(Level != 0):
						setTemp = int(str(Level).strip("0")) + 16
					Domoticz.Debug("Change Temp to " + str(setTemp) + " for "+device['name'])
					melSetTemp(device['id'],str(setTemp))
					Devices[Unit].Update(Devices[Unit].nValue, str(Level))
				else:
					Domoticz.Debug("Device not found")
	return True
 
def onHeartbeat():
	global melDevices,domLevelTemp,domLevelFan
	Domoticz.Debug("Current MEL Cloud Key ID:"+melKey)
	Domoticz.Debug("Current MEL Building ID:"+ str(melBuildingID))
	for device in melDevices:
		melDeviceStatus = melGetStatus(device['id'])
		domCurrentTemp = domLevelTemp[Devices[device['thermoID']].sValue]
		domCurrentFan = domLevelFan[Devices[device['fanID']].sValue]
		domCurrentVaneH = domLevelVaneH[Devices[device['vaneHozID']].sValue]
		domCurrentVaneV = domLevelVaneV[Devices[device['vaneVerID']].sValue]
		Domoticz.Debug("******** "+device['name']+" ********")
		Domoticz.Debug("Sync POWER " + str(melDeviceStatus['Power']) \
			+ " OPERATION MODE " + str(melDeviceStatus['OperationMode']) \
			+ " FAN SPEED " + str(melDeviceStatus['SetFanSpeed']) \
			+ " VANE HOZ " + str(melDeviceStatus['VaneHorizontal']) \
			+ " VANE VER " + str(melDeviceStatus['VaneVertical']) \
			+ " UNIT TEMP " + str(melDeviceStatus['SetTemperature']))
		if(melDeviceStatus['Power'] is True and Devices[device['modeID']].nValue == 0):
			sValue = str(domModeLevels.index(melDeviceStatus['OperationMode'])) + "0"
			Devices[device['modeID']].Update(1,sValue,domModePic[domModeLevels.index(melDeviceStatus['OperationMode'])])
			Devices[device['fanID']].Update(1,str(Devices[device['fanID']].sValue))
			Devices[device['thermoID']].Update(1,str(Devices[device['thermoID']].sValue))
			Devices[device['vaneHozID']].Update(1,str(Devices[device['vaneHozID']].sValue))
			Devices[device['vaneVerID']].Update(1,str(Devices[device['vaneVerID']].sValue))
		elif(melDeviceStatus['Power'] is False and Devices[device['modeID']].nValue == 1):
			Devices[device['modeID']].Update(0,"0",9)
			Devices[device['fanID']].Update(0,str(Devices[device['fanID']].sValue))
			Devices[device['vaneHozID']].Update(0,str(Devices[device['vaneHozID']].sValue))
			Devices[device['vaneVerID']].Update(0,str(Devices[device['vaneVerID']].sValue))
 
# Sync fan value from MELCloud to Domoticz
		if(melDeviceStatus['SetFanSpeed'] != domCurrentFan):
			for level, fan in domLevelFan.items():
				if(fan == melDeviceStatus['SetFanSpeed']):
					Devices[device['fanID']].Update(Devices[device['fanID']].nValue,level)
# Sync Unit temperature value from MELCloud to Domoticz
		if(melDeviceStatus['SetTemperature'] != domCurrentTemp):
			for level, temp in domLevelTemp.items():
				if(temp == melDeviceStatus['SetTemperature']):
					Devices[device['thermoID']].Update(Devices[device['thermoID']].nValue,level)
# Sync Vane Horizontal value from MELCloud to Domoticz
		if(melDeviceStatus['VaneHorizontal'] != domCurrentVaneH):
			for level, vaneH in domLevelVaneH.items():
				if(vaneH == melDeviceStatus['VaneHorizontal']):
					Devices[device['vaneHozID']].Update(Devices[device['vaneHozID']].nValue,level)
# Sync Vane Vertical value from MELCloud to Domoticz
		if(melDeviceStatus['VaneVertical'] != domCurrentVaneH):
			for level, vaneV in domLevelVaneV.items():
				if(vaneV == melDeviceStatus['VaneVertical']):
					Devices[device['vaneVerID']].Update(Devices[device['vaneVerID']].nValue,level)
# Sync Room temperature value from MELCloud to Domoticz
		if(str(melDeviceStatus['RoomTemperature']) != Devices[device['roomTempID']].sValue):
			Devices[device['roomTempID']].Update(1,str(melDeviceStatus['RoomTemperature']))
			Domoticz.Debug("Sync ROOM TEMP" + str(melDeviceStatus['RoomTemperature']))
# Sync Infos value from MELCloud to Domoticz
		textInfos = "NEXT UPDATE " +  str(melDeviceStatus['NextCommunication']) + "MAC ADDR " + device['macaddr'] + " S/N " + device['sn']
		if(textInfos != str(Devices[device['infosID']].sValue)):
			Devices[device['infosID']].Update(1,textInfos)
		Domoticz.Debug("Infos " + str(Devices[device['infosID']].sValue))
	return True
 
def onStop():
	Domoticz.Debug("Goobye from MELCloud plugin :)")
	return True
def stringToBase64(s):
    return base64.b64encode(s.encode('utf-8')).decode("utf-8")


Put this file in directory Melcloud under DOMOTICZ_DIR/plugins

Configuration

You can add devices by going to hardware.