#!/usr/bin/perl # # This script provides a CLI control and monitoring interface for AirconWithMe # branded IntesisSmartHome devices. In this case, only the functions required # for a Mitsubishi Heavy Industries split unit A/C have been implemented. # # Requires Debian packages: libjson-perl (JSON on CPAN) # # "1": ["On/Off", "TEXT_VALUES_ONOFF", ""], # "2": ["User Mode", "TEXT_VALUES_MODES", ""], # "4": ["Fan Speed", "TEXT_VALUES_FANSPEED", ""], # "5": ["Vane Up/Down Position", "TEXT_VALUES_VANES", ""], # "6": ["Vane Left/Right Position", "TEXT_VALUES_VANES", ""], # "9": ["User Setpoint", {}, "°C"], # "10": ["Return Path Temperature", {}, "°C"], # "12": ["Remote Disable", "TEXT_VALUES_REMOTE", ""], # "13": ["On Time", {}, "h"], # "14": ["Alarm Status", "TEXT_VALUES_ONOFF", ""], # "15": ["Error Code", {}, ""], # "34": ["Quiet Mode", "TEXT_VALUES_ONOFF", ""], # "35": ["Min Temperature Setpoint", {}, "°C"], # "36": ["Max Temperature Setpoint", {}, "°C"], # "37": ["Outdoor Temperature", {}, "°C"], # "181": ["Maintenance time", {}, "h"], # "182": ["Maintenance config", {}, "h"], # "183": ["Maintenance Filter time", {}, "h"], # "184": ["Maintenance Filter config", {}, "h"] # # "TEXT_VALUES_ONOFF": # "0":"Off" # "1":"On" # "TEXT_VALUES_MODES": # "0":"Auto" # "1":"Heat" # "2":"Dry" # "3":"Fan" # "4":"Cool" # "TEXT_VALUES_FANSPEED": { # "0":"Auto" # "1":"Speed 1" # "2":"Speed 2" # "3":"Speed 3" # "4":"Speed 4" # "5":"Speed 5" # "6":"Speed 6" # "7":"Speed 7" # "8":"Speed 8" # "9":"Speed 9" # "10":"Speed 10" # "TEXT_VALUES_VANES": { # "0":"Auto" # "1":"Position 1" # "2":"Position 2" # "3":"Position 3" # "4":"Position 4" # "5":"Position 5" # "6":"Position 6" # "7":"Position 7" # "8":"Position 8" # "9":"Position 9" # "10":"Swing" # "11":"Swirl" # "12":"Wide" # "TEXT_VALUES_REMOTE": { # "0":"Remote Enabled" # "1":"Remote Disabled" # # PelliX 2018 # use strict; use warnings; use HTTP::Request::Common qw(POST); use LWP::UserAgent; use Term::ANSIColor; use JSON qw( decode_json ); # custom vars my $userName = "admin"; my $passWord = "XXXXXX"; # global variables my $bDebug = 0; # should be 0, ideally my $getValue; my $dumpValue; my $remoteHost; my $itemUid; my $itemValue; my $sessionId; my $opMode = "getval"; # setval, getdump # get command line args # print usage if no or too few arguments were given if ($#ARGV < 2) { &usage(); } &getCommandArgs(); # program logic if ($opMode eq "getval") { # get open info my $result = &getRequest($remoteHost, $getValue); # get restricted info &getSessionId($remoteHost, $userName, $passWord); my @restrictedresults = &getRestrictedRequest($remoteHost, $sessionId, $getValue); foreach (split(/\n/,$result)) { # careful with splitting the string by colons because of MAC addrs my ($fldname, $fldvalue) = (split(/\:/,$_,2))[0,1]; #print "Matching " . $fldname . " with " . $getValue . "\n\n"; if ($fldname and $fldname eq "\"$getValue\"") { #print "**$fldname**\t$fldvalue\n"; $fldvalue =~ s/\"//g; print $fldvalue . "\n"; } } my $i = 0; foreach (@restrictedresults) { if ($restrictedresults[$i][0] and $restrictedresults[$i][0] eq "$getValue") { print $restrictedresults[$i][1] . "\n"; } $i++ } } if ($opMode eq "getdump") { my $result = &getRequest($remoteHost, ""); foreach (split(/\n/,$result)) { my ($fldname, $fldvalue) = (split(/\:/,$_,2))[0,1]; #print "**$fldname**\t$fldvalue\n"; if ($fldvalue) { $fldvalue =~ s/\"//g; $fldname =~ s/\"//g; print $fldname . " : " . $fldvalue . "\n"; } } &getSessionId($remoteHost, $userName, $passWord); my @restrictedresults = &getRestrictedRequest($remoteHost, $sessionId); #print $result; my $i = 0; foreach (@restrictedresults) { if ($restrictedresults[$i][0]) { print $restrictedresults[$i][0] . " : "; print $restrictedresults[$i][1] . "\n"; } $i++ } } if ($opMode eq "setval") { &getSessionId($remoteHost, $userName, $passWord); my $result; if ($itemUid =~ m/^\d+/) { $result = &postRestrictedRequest($remoteHost, $sessionId, $itemUid, $itemValue); } elsif ($itemUid eq "Identify") { $result = &postIdentifyRequest($remoteHost, $sessionId); } else { if ($itemUid eq "Power") { $itemUid = "1" }; if ($itemUid eq "User Mode") { $itemUid = "2" }; if ($itemUid eq "Fan Speed") { $itemUid = "4" }; if ($itemUid eq "Vane Up/Down Position") { $itemUid = "5" }; if ($itemUid eq "Setpoint Temperature") { $itemUid = "9" }; if ($itemUid eq "Return Path Temperature") { $itemUid = "10" }; if ($itemUid eq "Remote Disable") { $itemUid = "12" }; $result = &postRestrictedRequest($remoteHost, $sessionId, $itemUid, $itemValue); } print $result . "\n"; } exit; # subroutines # parse command line arguments sub getCommandArgs() { # special syntaxes my $debugarg = "--debug"; my $helparg = "-i"; my $output; # -g, -p or -d are required, bail if not supplied my $o = 0; my $i = 0; my $validargs; my $hostset; while($o < $#ARGV || $o == $#ARGV) { if ($ARGV[$o] eq "-r") { $hostset = 1; } if ($ARGV[$o] eq "-g" or $ARGV[$o] eq "-p" or $ARGV[$o] eq "-d") { $validargs = 1; } $o++; } if (!$hostset) { &printColored("\nNo host defined!\n"); &usage(); } if (!$validargs) { &usage(); } while($i < $#ARGV || $i == $#ARGV) { my $argument = $ARGV[$i]; if ($argument eq $debugarg) { $bDebug = 1; } if($argument eq $helparg) { usage(); } if ($bDebug == 1) { $output = "Arg[".$i."] = " . $argument . " \n"; &printDebug($output); } if ($argument eq "-g") { $opMode = "getval"; $getValue = $ARGV[($i+1)]; if ($bDebug == 1) { &printColored("GETting value: " . $getValue . "\n"); } } if ($argument eq "-r") { $remoteHost = $ARGV[($i+1)]; if ($bDebug == 1) { &printColored("Using host: " . $remoteHost . "\n"); } } if ($argument eq "-d") { $opMode = "getdump"; } if ($argument eq "-p") { $itemUid = $ARGV[($i+1)]; if ($bDebug == 1) { &printColored("Setting UID: " . $itemUid . "\n"); } $opMode = "setval"; } if ($argument eq "-v") { $itemValue = $ARGV[($i+1)]; if ($bDebug == 1) { &printColored("Providing value: " . $itemValue . "\n"); } } $i++; } } # send a GET (value) request to device sub getRequest() { my $uri = 'http://'. $_[0] .'/api.cgi'; my $json = '{"command":"getinfo","data":"'.$_[1].'"}'; if ($bDebug == 1) { &printDebug("JSON Request: " . $json . "\n"); } #my $json = '{"command":"getinfo","data":""}'; # the API returns the whole list anyway, regardless my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; my $response = $lwp->request( $req ); $response = $response->decoded_content; if ($bDebug == 1) { &printDebug("JSON Response: " . $response . "\n"); } $response =~ s/[,|\{|\}]/\n/g; #print $response; return $response; } sub getRestrictedRequest() { my $uri = 'http://'. $_[0] .'/api.cgi'; my $json = '{"command":"getdatapointvalue","data":{"sessionID":"'.$_[1].'","uid":"all"}}'; if ($bDebug == 1) { &printDebug("JSON Request: " . $json . "\n"); } #my $json = '{"command":"getinfo","data":""}'; # the API returns the whole list anyway, regardless my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; my $response = $lwp->request( $req ); $response = $response->decoded_content; # convert to JSON if ($bDebug == 1) { &printDebug("JSON Response: " . $response . "\n"); } $response = decode_json($response); # convert to hashed array my @uids = @{ $response->{'data'}{'dpval'} }; #print join(", ", @uids); my @datapoints; my $i = 0; foreach my $f ( @uids ) { # 1 == Power if ($f->{'uid'} eq "1") { $datapoints[$i][0] = "Power"; if ($f->{"value"} eq "0") { $datapoints[$i][1] = $f->{"value"} . " (Off)"; } else { $datapoints[$i][1] = $f->{"value"} . " (On)"; } } # 2 == User Mode if ($f->{'uid'} eq "2") { $datapoints[$i][0] = "User Mode"; if ($f->{"value"} eq "0") { $datapoints[$i][1] = $f->{"value"} . " (Auto)"; } elsif($f->{"value"} eq "1") { $datapoints[$i][1] = $f->{"value"} . " (Heat)"; } elsif($f->{"value"} eq "2") { $datapoints[$i][1] = $f->{"value"} . " (Dry)"; } elsif($f->{"value"} eq "3") { $datapoints[$i][1] = $f->{"value"} . " (Fan)"; } elsif($f->{"value"} eq "4") { $datapoints[$i][1] = $f->{"value"} . " (Cool)"; } } # 4 == Fan Speed if ($f->{'uid'} eq "4") { $datapoints[$i][0] = "Fan Speed"; if($f->{"value"} eq "1") { $datapoints[$i][1] = $f->{"value"} . " (Low)"; } elsif($f->{"value"} eq "2") { $datapoints[$i][1] = $f->{"value"} . " (Medium)"; } elsif($f->{"value"} eq "3") { $datapoints[$i][1] = $f->{"value"} . " (High)"; } elsif($f->{"value"} eq "4") { $datapoints[$i][1] = $f->{"value"} . " (Turbo)"; } } # 5 == Vanes Vertical if ($f->{'uid'} eq "5") { $datapoints[$i][0] = "Vane Up/Down Position"; if($f->{"value"} eq "1") { $datapoints[$i][1] = $f->{"value"} . " (Pos. 1)"; } elsif($f->{"value"} eq "2") { $datapoints[$i][1] = $f->{"value"} . " (Pos. 2)"; } elsif($f->{"value"} eq "3") { $datapoints[$i][1] = $f->{"value"} . " (Pos. 3)"; } elsif($f->{"value"} eq "4") { $datapoints[$i][1] = $f->{"value"} . " (Pos. 4)"; } elsif($f->{"value"} eq "10") { $datapoints[$i][1] = $f->{"value"} . " (Swing)"; } } # 9 == Setpoint Temperature if ($f->{'uid'} eq "9") { $datapoints[$i][0] = "Setpoint Temperature"; $datapoints[$i][1] = substr($f->{"value"}, 0, 2) . ".0 (Celsius)"; } # 10 == Return Path Temperature if ($f->{'uid'} eq "10") { $datapoints[$i][0] = "Return Path Temperature"; $datapoints[$i][1] = substr($f->{"value"}, 0, 2) . ".0 (Celsius)"; } # 12 == Remote Disable if ($f->{'uid'} eq "12") { $datapoints[$i][0] = "Remote Disable"; if ($f->{"value"} eq "0") { $datapoints[$i][1] = $f->{"value"} . " (Off)"; } else { $datapoints[$i][1] = $f->{"value"} . " (On)"; } } # 13 == On Time if ($f->{'uid'} eq "13") { $datapoints[$i][0] = "On Time"; $datapoints[$i][1] = $f->{"value"} . " (Hours)"; } # 14 == Alarm Status if ($f->{'uid'} eq "14") { $datapoints[$i][0] = "Alarm Status"; if ($f->{"value"} eq "0") { $datapoints[$i][1] = $f->{"value"} . " (Off)"; } else { $datapoints[$i][1] = $f->{"value"} . " (On)"; } } # 15 == Error Code if ($f->{'uid'} eq "15") { $datapoints[$i][0] = "Error Code"; $datapoints[$i][1] = $f->{"value"} . " (Code)"; } # 35 = min setpoint, 36 = max setpoint # 37 == Outdoor Temperature if ($f->{'uid'} eq "37") { $datapoints[$i][0] = "Outdoor Temperature"; $datapoints[$i][1] = substr($f->{"value"}, 0, 2) . ".0 (Celsius)"; } # 181 == Maintenance Time if ($f->{'uid'} eq "181") { $datapoints[$i][0] = "Maintenance time"; $datapoints[$i][1] = $f->{"value"} . " (Hours)"; } # 182 == Maintenance Time if ($f->{'uid'} eq "182") { $datapoints[$i][0] = "Maintenance config"; $datapoints[$i][1] = $f->{"value"} . " (Hours)"; } # 183 == Maintenance Time if ($f->{'uid'} eq "183") { $datapoints[$i][0] = "Maintenance Filter time"; $datapoints[$i][1] = $f->{"value"} . " (Hours)"; } # 184 == Maintenance Time if ($f->{'uid'} eq "184") { $datapoints[$i][0] = "Maintenance Filter config"; $datapoints[$i][1] = $f->{"value"} . " (Hours)"; } $i++; } return @datapoints; } sub postRestrictedRequest() { my $uri = 'http://'. $_[0] .'/api.cgi'; #my $json = '{"command":"setdatapointvalue","data":{"sessionID":"'.$_[1].'","uid":"'.$_[2].'","value":"'.$_[3].'"}}'; my $json = '{"command":"setdatapointvalue","data":{"sessionID":"'.$_[1].'","uid":'.$_[2].',"value":'.$_[3].'}}'; if ($bDebug == 1) { &printDebug("JSON Request: " . $json . "\n"); } my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; my $response = $lwp->request( $req ); $response = $response->decoded_content; if ($bDebug == 1) { &printDebug("JSON Response: " . $response . "\n"); } $response = decode_json($response); $response = $response->{"success"}; return $response; } sub postIdentifyRequest() { my $uri = 'http://'. $_[0] .'/api.cgi'; #my $json = '{"command":"setdatapointvalue","data":{"sessionID":"'.$_[1].'","uid":"'.$_[2].'","value":"'.$_[3].'"}}'; my $json = '{"command":"identify","data":{"sessionID":"'.$_[1].'"}}'; if ($bDebug == 1) { &printDebug("JSON Request: " . $json . "\n"); } my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; my $response = $lwp->request( $req ); $response = $response->decoded_content; if ($bDebug == 1) { &printDebug("JSON Response: " . $response . "\n"); } $response = decode_json($response); $response = $response->{"success"}; return $response; } # get a session ID (authenticate) sub getSessionId() { my $uri = 'http://'. $_[0] .'/api.cgi'; my $json = '{"command":"login","data":{"username":"'.$_[1].'","password":"'.$_[2].'"}}'; if ($bDebug == 1) { &printDebug("JSON Request: " . $json . "\n"); } my $req = HTTP::Request->new( 'POST', $uri ); $req->header( 'Content-Type' => 'application/json' ); $req->content( $json ); my $lwp = LWP::UserAgent->new; my $response = $lwp->request( $req ); $response = $response->decoded_content; if ($bDebug == 1) { &printDebug("JSON Response: " . $response . "\n"); } $response =~ s/[,|\{|\}]/\n/g; if ($response =~ m/\"success\":false/) { &printError("Authentication failed. Exiting.\n"); exit; } # get session ID foreach (split(/\n/,$response)) { my ($fldname, $fldvalue) = (split(/\:/,$_))[0,1]; #print "Matching " . $fldname . " with \"sessionID\"\n\n"; if ($fldname and $fldname eq "\"sessionID\"") { #print "**$fldname**\t$fldvalue\n"; $fldvalue =~ s/\"//g; $sessionId = $fldvalue; if ($bDebug == 1) { &printDebug($fldvalue); } } } #print $sessionId; return $sessionId; } # print colored text to console sub printColored() { # maybe another color scheme wouldn't hurt print colored ['green on_black'], shift; } # print (red) error text to console sub printError() { # maybe another color scheme wouldn't hurt print colored ['red on_black'], "ERROR: " . shift; } # print debug text to console sub printDebug() { # maybe another color scheme wouldn't hurt print colored ['black on_white'], "DEBUG: " . shift; } # print script usage sub usage() { print STDERR << "EOF"; usage: $0 [-g|-p -v value] [-d] -r host [--debug] -g : Get a value from the device -p : Post a command/value to the device -d : Dump all values from the device -h : Show this information -r : Remote host to connect to --debug : Print debug output from script -g, -p or -d and -r are required example: $0 -g fanspeed -r airco1 example: $0 -d -r airco1 example: $0 -p 5 -v 2 -r airco1 example: $0 -h API items: "wlanSTAMAC" "wlanAPMAC" "ownSSID" "fwVersion" "wlanFwVersion" "wlanLNK" "ssid" "rssi" "tcpServerLNK" "localdatetime" "deviceModel" "sn" Restricted items: "Power" "User Mode" "Fan Speed" "Vane Up/Down Position" "Vane Left/Right Position" "User Setpoint" "Return Path Temperature" "Remote Disable" "On Time" "Alarm Status" "Error Code" "Min Temperature Setpoint" "Max Temperature Setpoint" "Outdoor Temperature" "Maintenance time" "Maintenance config" "Maintenance Filter time" "Maintenance Filter config" EOF exit; }