Simple remote control utility for Gree Smart air conditioners
This project aims to provide an open-source library for controlling Gree Smart Air Conditioners. The implementation is based on the reverse-engineered proprietary, JSON-based protocol used by these units. Also there are remote control app implementations for multiple platforms:
You can find more implementations for other languages and frameworks:
The first step is to clone this repository: git clone https://github.com/tomikaa87/gree-remote.git
.
Don't forget to checkout all the submodules using git submodule update --init --recursive
.
Qt application:
3rdparty
. This is only necessary for the native C++/Qt library, Android uses Java's crypto library.GreeRemote.pro
from the root of the checkout directoryAndroid application:
GreeRemoteAndroid
in Android StudioFor the Qt library and application:
For the Android application:
For the macOS application:
This project is in a very early development stage. Currently only a basic device discovery and binding is implemented.
There is so much to do:
This project is licensed under the GPL License - see the LICENSE file for details
I would like to thank the additional work to:
This information is based on the implementation of the official Gree Smart Android App and the network packets obtained via Wireshark. The current implementation is incomplete, for example it doesn't have the ability to update the firmware on the AC unit.
The communication protocol uses unicast and broadcast UDP messages sent to port 7000.
The protocol uses pack
-type messages to deliver data from and to the device in a (somewhat) secure way. This message contains a field named pack
, which encapsulates an another JSON object.
Packs created in the following way:
Decoding a pack is the same process, but in reverse order. The generic AES key is used for reading scan results and binding devices, the device-specific key is used for direct communication (requesting status, changing parameters etc.).
In order to find all the devices on the network, a scan packet must be broadcasted. This package is a very simple JSON object:
{
"t": "scan"
}
All connected devices will send a response JSON like this one:
{
"t": "pack",
"i": 1,
"uid": 0,
"cid": "<device's MAC address, e.g. 00123456789a>",
"tcid": "",
"pack": "<base64 encoded, encrypted data>"
}
This is a generic pack
-type response which has a pack
field that contains an embedded JSON object. The pack
is encrypted with AES128/ECB and encoded in Base64. This response is encrypted using the "Generic AES key" which is the same for all devices.
Contents of pack
should look like this:
{
"t": "dev",
"cid": "<MAC address>",
"bc": "gree",
"brand": "gree",
"catalog": "gree",
"mac": "<MAC address>",
"mid": "10001",
"model": "gree",
"name": "<friendly name of the unit>",
"series": "gree",
"vender": "1",
"ver": "V1.1.13",
"lock": 0
}
You can obtain some basic information (e.g. device's friendly name, software version etc.) from this object.
In order to communicate with a specific device and obtain the device's unique encryption key, you must bind to it using the following request JSON:
{
"cid": "app",
"i": 1,
"pack": "<encrypted, encoded pack>",
"t": "pack",
"tcid": "<MAC address>",
"uid": 0
}
pack
must have the following content:
{
"mac": "<MAC address>",
"t": "bind",
"uid": 0
}
If the binding request succeeds, you should have the following response:
{
"t": "pack",
"i": 1,
"uid": 0,
"cid": "<MAC address>",
"tcid": "app",
"pack": "<encrypted, encoded pack>"
}
The pack
of this response should look like this:
{
"t": "bindok",
"mac": "<MAC address>",
"key": "<unique AES key>",
"r": 200
}
The AES key in the key
field is used to send control packets to a specific device.
To get the status of the device, a generic pack
type request must be sent to it:
{
"cid": "app",
"i": 0,
"pack": "<encrypted, encoded pack>",
"t": "pack",
"tcid": "<MAC address>",
"uid": 0
}
The pack
of this request must contain a status request object:
{
"cols": [
"Pow",
"Mod",
"SetTem",
"WdSpd",
"Air",
"Blo",
"Health",
"SwhSlp",
"Lig",
"SwingLfRig",
"SwUpDn",
"Quiet",
"Tur",
"StHt",
"TemUn",
"HeatCoolType",
"TemRec",
"SvSt"
],
"mac": "<MAC address>",
"t": "status"
}
In this object you must define which parameters you are interested in. All of them has a numerical value. The official Gree Smart app uses these fields:
Pow
: power state of the device
Mod
: mode of operation
"SetTem" and "TemUn": set temperature and temperature unit
TemUn
= 0, SetTem
is the set temperature in CelsiusTemUn
= 1, SetTem
is the set temperature is FahrenheitWdSpd
: fan speed
Air
: controls the state of the fresh air valve (not available on all units)
Blo
: "Blow" or "X-Fan", this function keeps the fan running for a while after shutting down. Only usable in Dry and Cool mode
Health
: controls Health ("Cold plasma") mode, only for devices equipped with "anion generator", which absorbs dust and kills bacteria
SwhSlp
: sleep mode, which gradually changes the temperature in Cool, Heat and Dry mode
Lig
: turns all indicators and the display on the unit on or off
SwingLfRig
: controls the swing mode of the horizontal air blades (available on limited number of devices, e.g. some Cooper & Hunter units - thanks to mvmn)
SwUpDn
: controls the swing mode of the vertical air blades
Quiet
: controls the Quiet mode which slows down the fan to its most quiet speed. Not available in Dry and Fan mode.
Tur
: sets fan speed to the maximum. Fan speed cannot be changed while active and only available in Dry and Cool mode.
StHt
: maintain the room temperature steadily at 8°C and prevent the room from freezing by heating operation when nobody is at home for long in severe winter (from http://www.gree.ca/en/features)
HeatCoolType
: unknown
TemRec
: this bit is used to distinguish between two Fahrenheit values (see Setting the temperature using Fahrenheit section below)
SvSt
: energy saving mode
If the status request succeeds, you should have the following object in the response pack
:
{
"t": "dat",
"mac": "<MAC address>",
"r": 200,
"cols": [
"Pow",
"Mod",
"SetTem",
"WdSpd",
"Air",
"Blo",
"Health",
"SwhSlp",
"Lig",
"SwingLfRig",
"SwUpDn",
"Quiet",
"Tur",
"StHt",
"TemUn",
"HeatCoolType",
"TemRec",
"SvSt"
],
"dat": [1, 1, 25, 1, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0]
}
In this object, cols
defines the parameter names and dat
defines the values for them.
Since the device won't send you a status update packet when you change a setting using the remote control or the app, you should poll the it periodically.
In order to set a parameter of a device, you must send a command packet to it. It's a simple pack
-type object with the following JSON encoded into it:
{
"cid": "app",
"i": 0,
"pack": "<encrypted, encoded pack>",
"t": "pack",
"tcid": "<MAC address>",
"uid": 0
}
pack
:
{
"opt": ["TemUn", "SetTem"],
"p": [0, 27],
"t": "cmd"
}
In this object, opt
contains the names of the parameters you want to set and p
contains the values for them. The type of the pack is cmd
. If the request succeeds, you should have the following response pack:
{
"t": "pack",
"i": 0,
"uid": 0,
"cid": "<MAC address>",
"tcid": "",
"pack": "<encrypted, encoded pack>"
}
pack
:
{
"t": "res",
"mac": "<MAC address>",
"r": 200,
"opt": ["TemUn", "SetTem"],
"p": [0, 27],
"val": [0, 27]
}
In this object, r
is the response code (not sure if there are other values than 200 because the device won't send you anythin if the request fails]), opt
contains the name of the parameters you set, p
and val
contains the values for them.
Some firmwares may return only one field p
instead of both p
and val
. It is better to handle such cases.
Update: it seems that there are different variants of these Gree devices that properly respond to an invalid packet, probably the newer ones with updated firmware.
Two things I found were despite TemUn being set, the set temp is still in Celsius. Use the TemRec bit to distinguish between the two Fahrenheit temps
pack
:
{
"opt": ["TemUn", "SetTem","TemRec"],
"p": [1, 27,0],
"t": "cmd"
}
If the device is equipped with a temperature sensor, you can read it via the TemSen
key.
The value is in celsius and has an offset of +40 to avoid using negative values.
For example if you get 65
from the device it means the current temperature is 65 - 40 = 25
.
If you use the gree.py
script, you can read the sensor like this:
python3 gree.py -c <device ip> -i <device id> -k <device key> get TemSen
TempRec TemSet Mapping for setting Fahrenheit
Units | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Fahrenheit | 68. | 69. | 70. | 71. | 72. | 73. | 74. | 75. | 76. | 77. | 78. | 79. | 80. | 81. | 82. | 83. | 84. | 85. | 86. |
Celsius | 20.0 | 20.5 | 21.1 | 21.6 | 22.2 | 22.7 | 23.3 | 23.8 | 24.4 | 25.0 | 25.5 | 26.1 | 26.6 | 27.2 | 27.7 | 28.3 | 28.8 | 29.4 | 30.0 |
TemSet | 20 | 21 | 21 | 22 | 22 | 23 | 23 | 24 | 24 | 25 | 26 | 26 | 27 | 27 | 28 | 28 | 29 | 29 | 30 |
TemRec | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 |
Equations
TemSet = round((desired_temp_f - 32.0) * 5.0 / 9.0)
TemRec = (int) ((((desired_temp_f - 32.0) * 5.0 / 9.0) - TemSet) > 0)
There is a simple scheduler implementation which can turn on or off your device automatically. New scheduling item can be added via the following packet (thanks to oroce for the details):
{
"cmd": [
{
"mac": [
"<MAC address>"
],
"opt": [
"Pow"
],
"p": [
0
]
}
],
"enable": 0,
"hr": 20,
"id": 0,
"min": 40,
"name": "5363686564756c65",
"sec": 0,
"t": "setT",
"tz": 1,
"week": [
0,
0,
1,
0,
0,
1,
0
]
}
In this object, cmd
defines which device you want to address (mac
), which parameters you want to set (opt
) and which are the values for them (p
). enable
controls the state of this scheduling item, hr
and min
is the time, name
is the name of the item encoded into ASCII bytes in hexadecimal format, tz
is the time zone (probably an offset value) and week
defines on which weekdays the device will execute the command, begining with Sunday.
In order to get the current time of the device's clock, you must send the following encrypted pack to it:
pack
:
{
"cols": ["time"],
"mac": "<MAC address>",
"t": "status"
}
And the device will send a response like that:
pack
:
{
"t": "dat",
"mac": "<MAC address>",
"r": 200,
"cols": ["time"],
"dat": ["2018-05-11 19:42:01"]
}
To set the time on the device, send the following pack:
pack
:
{
"opt": ["time"],
"p": ["2018-05-11 19:29:38"],
"sub": "<MAC address>",
"t": "cmd"
}
And the device will send a response like that:
pack
:
{
"t": "res",
"mac": "<MAC address>",
"r": 200,
"opt": ["time"],
"p": ["2018-05-11 19:29:38"],
"val": ["2018-05-11 19:29:38"]
}
For the sake of simplicity, you can send device control messages to the broadcast address instead of the IP of the device, because the tcid
field addresses the device properly. With this little trick you can omit storing IP addresses for specific devices.
Different units have different firmware versions. Some of them have weird limitations. One of these can be a WiFi password length limitation. WPA supports 63-character long passwords, but some units limit this to 31 characters. Please be aware of this issue when your units can't connect to your network.
The WiFi controller in these devices has the ability to be controlled through the cloud. To be able to do that, they periodically send "heartbeat" packets to Gree servers which are located in China. If you are concerned about your privacy and want to block this communication, you have a few ways to do that:
138.91.51.53
to port 5000 TCP. This method can cause some units to lock up and stop responding to local requests.