After a while, i finnaly manage to control my mitsubishi units with perl script.
I share with you the code:
Code: Select all
#!/usr/bin/perl
#Dependency: libwww-perl, libjson-perl
###################################################
# Release notes
###################################################
#Version 0.1: First release (with very dirty code...)
# - switch on / off unit
# - report room temperature
# - report unit temperature set
# - report next unit update
###################################################
# INSTALLATION
###################################################
# Put scripts in Domoticz install dir under scripts
# Give execute write to the file
# Create dummy devices:
# - 1 switch for switch on/off device
# -> Action on scripts: script:///DOMOTICZ_DIR/scripts/melcloud.pl on MEL_DEVICENAME
# -> Action off scripts: script:///DOMOTICZ_DIR/scripts/melcloud.pl off MEL_DEVICENAME
# - 1 temp for reporting room temperature
# - 1 settemp for reporting temperature set on device
# - 1 text for reporting next update hour
# Add cron entrie
# */10 * * * * /DOMOTICZ_DIR/scripts/melcloud.pl report 2>&1 >> /dev/null
use HTTP::Request::Common qw(POST GET);
use HTTP::Headers;
use LWP::UserAgent;
use JSON;
use Data::Dumper;
#####################################################
# Configuration
#####################################################
my $Email = "EMAIL\@EMAIL"; # Email used to login to MELcloud
my $Password = "PASSWORD"; # Password used lo login to MELcloud
my $debug = 0; # 0 = no debug, 1 = debug
my $domIP = "DOM_IP"; # Domoticz IP
my $domPORT = "DOM_PORT"; # Domoticz PORT
my @devicesInfos; # List of all devices. Add block for new devices
#Device 1
$devicesInfos{"NAME_ON_MELCLOUD"}{"idx_status"} = "88"; # IDX of status device (type switch)
$devicesInfos{"NAME_ON_MELCLOUD"}{"idx_temp"} = "89"; # IDX of temperature device (type temp)
$devicesInfos{"NAME_ON_MELCLOUD"}{"idx_update"} = "92"; # IDX of update time device (type text)
$devicesInfos{"NAME_ON_MELCLOUD"}{"idx_setTemp"} = "90"; # IDX of set temp device (type set point)
#Device n
$devicesInfos{"Bib"}{"idx_status"} = "104";
$devicesInfos{"Bib"}{"idx_temp"} = "97";
$devicesInfos{"Bib"}{"idx_update"} = "112";
$devicesInfos{"Bib"}{"idx_setTemp"} = "108";
##########################################################
# Functions
##########################################################
sub debug {
my ($msg) = @_;
if ($debug) {
print $msg."\n";
}
}
sub getDOMDeviceStatus {
my ($idx) = @_;
my $url = "http://$domIP:$domPORT/json.htm?type=devices&rid=".$idx;
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:Status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
sub login {
#Parameters
my ($Email,$Password) = @_;
#Variables
my $AppVersion = "1.9.2.1";
my $Language = "7";
my $CaptchaChallenge = "";
my $CaptchaResponse = "";
my $Persist = "true";
my $url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Login/ClientLogin";
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
# file.cgi should just return the data sent for this test
# These seem to be like <input type=text name=A value=$A > off a form...
my $req = POST $url, [
AppVersion => "$AppVersion",
CaptchaChallenge => "$CaptchaChallenge",
CaptchaResponse => "$CaptchaResponse",
Email => "$Email",
Language => "$Language",
Password => "$Password",
Persist => "$Persist"
];
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
elsif(/\"ErrorId\":1/ig) {
return "Bad password\n";
}
elsif(/\"ErrorId\":null/ig) {
$result = $1 if /(?:ContextKey\":\")+(.*?)\"/s;
debug ("context ID: ".$result);
return $result;
}
else {
return "Unknow error";
}
}
sub getMELBuildingID {
#Parameters
my ($ContextKey,$devicename) = @_;
#Variables
my $url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/User/ListDevices";
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
$req->header("X-MitsContextKey" => $ContextKey);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
my $decoded = JSON->new->utf8(0)->decode($_);
my $result = "0";
foreach my $building (@$decoded) {
@build = @{$building->{'Structure'}{'Areas'}};
foreach my $area (@build) {
@devices = @{$area->{'Devices'}};
foreach my $device (@devices) {
if ( $device->{'DeviceName'} =~ $devicename) {
$result = $device->{'BuildingID'};
}
}
}
}
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
# debug("Device ID: ".Dumper($decoded));
return "$result";
}
}
sub getMELDeviceID {
#Parameters
my ($ContextKey,$devicename) = @_;
#Variables
my $url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/User/ListDevices";
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
$req->header("X-MitsContextKey" => $ContextKey);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
my $decoded = JSON->new->utf8(0)->decode($_);
my $result = "0";
foreach my $building (@$decoded) {
@build = @{$building->{'Structure'}{'Areas'}};
foreach my $area (@build) {
@devices = @{$area->{'Devices'}};
foreach my $device (@devices) {
if ( $device->{'DeviceName'} =~ $devicename) {
$result = $device->{'DeviceID'};
}
}
}
}
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
# debug("Device ID: ".Dumper($decoded));
return "$result";
}
}
sub listDevices {
#Parameters
my ($ContextKey) = @_;
#Variables
my $url = "https://app.melcloud.com/Mitsubishi.Wifi.Client/User/ListDevices";
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
$req->header("X-MitsContextKey" => $ContextKey);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $_;
debug("list devices: ".$result);
return "$result";
}
}
sub getAirConInfos {
#Parameters
my ($contextKey,$deviceID,$buildingID) = @_;
#Variables
my $url = 'https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/Get?id=' . $deviceID . '&buildingID=' . $buildingID;
#return $url;
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
$req->header("X-MitsContextKey" => $contextKey);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$json = $res->decoded_content;
my $decoded = JSON->new->utf8(0)->decode($json);
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
debug("device status : ". $decoded);
return $decoded;
}
}
sub setDomDeviceStatus {
#Parameters
my ($idDomDevice,$deviceInfos) = @_;
#Variables
$domStatus = getDOMDeviceStatus($idDomDevice);
if ($deviceInfos->{'Power'} =~ /true/ && $domStatus =~ /"Off"/) {
$switchcmd = "On";
my $url = "http://$domIP:$domPORT/json.htm?type=command¶m=switchlight&idx=".$idDomDevice."&switchcmd=".$switchcmd;
debug("send to domoticz: ".$url);
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
elsif ($deviceInfos->{'Power'} =~ /false/ && $domStatus =~ /"On"/) {
$switchcmd = "Off";
my $url = "http://$domIP:$domPORT/json.htm?type=command¶m=switchlight&idx=".$idDomDevice."&switchcmd=".$switchcmd;
debug("send to domoticz: ".$url);
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
else {
debug("send to domoticz: nothing to send");
}
}
sub setDomDeviceTempRoom {
#Parameters
my ($idDomDevice,$deviceInfos) = @_;
my $url = "http://$domIP:$domPORT/json.htm?type=command¶m=udevice&idx=".$idDomDevice."&nvalue=0&svalue=".$deviceInfos->{'RoomTemperature'};
debug("send to domoticz: ".$url);
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
sub setDomDeviceNextUpdate {
#Parameters
my ($idDomDevice,$deviceInfos) = @_;
@time = split(/T/,$deviceInfos->{'NextCommunication'});
my $url = "http://$domIP:$domPORT/json.htm?type=command¶m=udevice&idx=".$idDomDevice."&nvalue=0&svalue=".$time[1];
debug("send to domoticz: ".$url);
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
sub setDomDeviceTempClim {
#Parameters
my ($idDomDevice,$deviceInfos) = @_;
my $url = "http://$domIP:$domPORT/json.htm?type=command¶m=udevice&idx=".$idDomDevice."&nvalue=0&svalue=".$deviceInfos->{'SetTemperature'};
debug("send to domoticz: ".$url);
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(GET => $url);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
if (/Illegal Operation/ig || $err != 200) {
return "Server returned error: $err\n";
}
else {
$result = $1 if /(?:status\"\ :)+(.*?),/s;
debug("receive from domoticz: ".$result);
return $result;
}
}
sub setMELDeviceStatus {
#Parameters
my ($contextKey,$status,$deviceInfos,$idx) = @_;
if($status =~ /on/ ) {
$deviceInfos->{'Power'} = "true";
}
elsif($status =~ /off/ ) {
$deviceInfos->{'Power'} = "false";
}
$deviceInfos->{'EffectiveFlags'} = "1";
$deviceInfos->{'HasPendingCommand'} = "true";
$json_device = JSON->new->utf8->encode($deviceInfos);
#Variables
my $url = 'https://app.melcloud.com/Mitsubishi.Wifi.Client/Device/SetAta';
#return $url;
# set up the stuff
my $ua = LWP::UserAgent->new();
# Set our own user-agent string!
$ua->agent("Domoticz Gysmo");
require HTTP::Request;
$req = HTTP::Request->new(POST => $url);
$req->header("X-MitsContextKey" => $contextKey);
$req->header('content-type' => 'application/json');
$req->content($json_device);
# Fire the cannon now !
my $res = $ua->request($req);
# Get the error back from the server if any
my $err = $res->status_line;
# Get server body text, $_ used in regexp on next line
$_ = $res->decoded_content;
my $decoded = JSON->new->utf8(0)->decode($_);
if (/Illegal Operation/ig || $err != 200) {
print "Server returned error: $err\n";
}
else {
#Send next update time to domoticz
setDomDeviceNextUpdate($idx,$decoded);
@time = split(/T/,$decoded->{'NextCommunication'});
debug("Update device : ".$time[1] );
return $result;
}
}
#####################################################
# Main program
#####################################################
#####################################################
# Init deviceID from Name
#####################################################
# TODO INIT GET ALL DEVICES INFORMATION FROM MELCloud
$contextID = login($Email,$Password);
foreach my $name (keys %devicesInfos) {
$buildingID = getMELBuildingID($contextID,$name);
$deviceID = getMELDeviceID($contextID,$name);
}
#####################################################
# Arguments
#####################################################
if ($ARGV[0] =~ /report/) {
foreach my $name (keys %devicesInfos) {
debug("############################");
debug("# Reporting device: " . $name ." #");
debug("############################");
$infosAirCon = getAirConInfos($contextID,$deviceID,$buildingID);
setDomDeviceTempRoom($devicesInfos{$name}{"idx_temp"},$infosAirCon);
setDomDeviceNextUpdate($devicesInfos{$name}{"idx_update"},$infosAirCon);
setDomDeviceTempClim($devicesInfos{$name}{"idx_setTemp"},$infosAirCon);
setDomDeviceStatus($devicesInfos{$name}{"idx_status"},$infosAirCon);
}
}
elsif ($ARGV[0] =~ /on/ && exists $devicesInfos{$ARGV[1]}{'idx_status'}) {
$infosAirCon = getAirConInfos($contextID,$deviceID,$buildingID);
setMELDeviceStatus($contextID,"on",$infosAirCon,$devicesInfos{$ARGV[1]}{'idx_update'});
}
elsif ($ARGV[0] =~ /off/ && exists $devicesInfos{$ARGV[1]}{'idx_status'}) {
$infosAirCon = getAirConInfos($contextID,$deviceID,$buildingID);
setMELDeviceStatus($contextID,"off",$infosAirCon,$devicesInfos{$ARGV[1]}{'idx_update'});
}
elsif ($ARGV[0] =~ /help/) {
print "report: send status of devices to domoticz. Use it for crontab\n";
print "on DEVICENAME: switch on a device. Use it with Action On.\n";
print "off DEVICENAME: switch off a device. Use it with Action Off\n";
print "more to come...\n";
}
else {
print "Nothing to do. Type help for help ;)\n";
}
My script connect to MELCloud website to get and change value to units.
There is a delay between switch on/off action and real switch on/off. The delay is reported to text device.
There is the same delay if you use Official MELCloud App or website.
For this first release, it only switch on/off unit an report some information.
I plan to add functionality like set temp, ventilation, cool or head and optimize code....
I am not a develloper so the code should be ugly like my english