Nice Garage Door Binding request

on page 10: https://www.niceforyou.com/sites/default/files/upload/manuals/idv0512a01en.pdf

IFTTT is described
could you use that to integrate to OH2?

Ok, so you have a connection, but do you know the password to authenticate and/or can you send commands successfully from within your Java environment? If not, presume that you are not looking for a binding to be developed, more that you first need the protocol and its encryption to be reverse engineered!

Jab

Of course I tried, I just couldn’t get it to work, the gate doesn’t react to the commands from IFTTT, although the applet test passes successfully (“Check now” button).

In android app source code I found authentication and command send algorithm, I checked it in IntellijIDEA - it’s works.

Any success communicating with that thing ? I’ll get one soon also

Nothing there right now.

I want to share what I found. I installed official app for Android, established a connection between it and IT4WIFI. Here is the logs of communication (rename it from xml to txt):
162E9DF9383_TLSv1.2_443_34930__.xml (14.6 KB)
162EDDD4A03_TLSv1.2_443_57914__.xml (21.2 KB)

The source code of the application can be obtained by decompiling the APK file, I do not know whether it can be distributed.

Hey there, got something new ? I was wondering to install the IT4WIFI also in my Nice Gate, but firstly i want to be sure i can integrate it in OH. Thanks, waiting for something new.

Hi Andrea,

After the last update of the Nice Welcome application, I managed to connect the gate to the IFFFT, but there you can only open or close the gate, when in Nice Welcome app you can open, close and stop the gate, as well as find out the current gate status.

2 Likes

I figured out how the application works and I want to share with you, maybe someone will write the binding.

I will describe it based on the source code of the application, although the version is two years ago, but little has changed since that time.

For the successful work of my examples, you need a phone with the Nice Welcom application installed, in which the gates with the IT4WIFI module are connected and configured. This is necessary to authorize the user who will be created by the script.

Consider the communication protocol of the device with the program on the phone:

  1. A request is sent to the device to verify the user’s existence:
<Request id="256" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="VERIFY">
<User username="Nice"/></Request>

id=“256” - Request ID
source=“986428c0d54ce5f2” - client ID
target=“00:0B:6C:11:22:33” - IT4WIFI MAC address
type=“VERIFY” - command type
User username=“Nice” - our Username
The device responds:

<Response  id="256" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="VERIFY">
<Authentication id="3" username="Nice" perm="user" notify="FALSE"/>
</Response>

id=“3” - Session ID
username=“Nice” - our Username
perm=“user” - User permissions, user or admin
It is important to pay attention to the session ID; based on it, the IDs of all our requests are generated.

  1. If we dont have a valid user, we need to pair our phone with device:
<Request id="3585" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="PAIR">
<Authentication username="Nice" cc="null" CType="phone" OSType="Android" OSVer="6.0.1" desc="my phone" /></Request>

username=“Nice” - new user Username, must be uniq
desc=“my phone” - user description
We will get response and event:

<Response  id="3585" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="PAIR">
<Authentication id="0" username="Nice" pwd="0MfLL0Q1AQBbI2MekelyQs2pMClff6t4dY0wJCjvzGc=">
</Authentication>
</Response><Event id="70" source="00:0B:6C:11:22:33" target="58c91234ea321d34" protocolType="NHK" protocolVersion="1.0" type="USER_NEW">
<Timestamp>2018-04-22T18:00:55</Timestamp>
<User id="2" controllerID="58c91234ea321d34" username="Nice" CType="phone" OSType="OS_unknown" OSVer="6.0.1" desc="Me"/>
</Event>
<Event id="71" source="00:0B:6C:11:22:33" target="58c91234ea321d34" protocolType="NHK" protocolVersion="1.0" type="USER_EDIT">
<Timestamp>2018-04-22T18:01:01</Timestamp>
</Event>

Most important thing in response is pwd, it will be needed in the future to sign messages:
pwd=“0MfLL0Q1AQBbI2MekelyQs2pMClff6t4dY0wJCjvzGc=”
To authorize user on device we need our phone with Nice Welcome installed.

  1. If the device answered to VERIFY request successfully, then a connection request is sent:
<Request id="1024" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CONNECT">
<Authentication username="Nice" cc="92E9F2A7"/></Request>

Here, CC is ClientChallenge, a randomly generated sequence on the phone, it will be needed in the future to sign messages.
In the case of a successful connection, we get a response containing the session ID, the name of the authorized user and the SC is ServerChallenge, the device generates it based on our CC:

<Response  id="1024" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CONNECT">
<Authentication id="1" username="Nice" sc="1C2099D9">
</Authentication>
</Response>
  1. Now you can request the status of the gate:
<Request id="2049" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="STATUS">
<Sign>Qr6ikOXG/VrCm5Unfa59rQjQXnf+YK+oFCkw48UIscs=</Sign></Request>

Here we see the Sign tag, this is the signature of the message. pwd, CC and SC used for sign building.
The answer would be:

<Response  id="2049" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="STATUS">
<Interface>
<Date>2018-04-21T23:24:09</Date>
<Settings>
<Name>Entrance</Name>
<Location>0,0</Location>
</Settings>
<Events>
<LastEvent>3</LastEvent>
</Events>
</Interface>
<Devices>
<Device id="1">
<Properties>
<DoorStatus>closed</DoorStatus>
<Obstruct>0</Obstruct>
</Properties>
<Events>
<LastEvent>57</LastEvent>
</Events>
<Scheduled>
</Scheduled>
</Device>
</Devices>
</Response>

Here the most important thing for us is the DoorStatus of the gate: Closed, Stopped, Open. Device ID also needed for CHANGE request (see below).

  1. And the most important request is a state change. Gate opening:
<Request id="4353" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
<Devices><Device id="1">
<Services><DoorAction>open</DoorAction>
</Services ></Device></Devices><Sign>PhAkG7OPsG0+GmIYXHBSXXZGCbSGsrUcJlNdi3fLmuE=</Sign></Request>

The device will send a response, and then there will be one or two events: the first event is sent immediately after the command is executed, in which we will see the current status of the gate:

<Response  id="4353" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
</Response><Event id="41" source="00:0B:6C:11:22:33" target="58c91234ea321d34" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
<Timestamp>2018-04-21T23:25:56</Timestamp>
<Devices>
<Device id="1">
<Properties>
<DoorStatus>opening</DoorStatus>
<Obstruct>0</Obstruct>
</Properties>
</Device>
</Devices>
</Event>

The device will send the second event only if the gate is fully opened, that the gate has been successfully opened:

<Event id="42" source="00:0B:6C:11:22:33" target="58c91234ea321d34" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
<Timestamp>2018-04-21T23:26:08</Timestamp>
<Devices>
<Device id="1">
<Properties>
<DoorStatus>opened</DoorStatus>
<Obstruct>0</Obstruct>
</Properties>
</Device>
</Devices>
</Event>
  1. A similar behavior will be in case of opening and stopping the gate.

In the next message I will show how this is implemented in the program.

2 Likes

How android application works.

  1. First, connect to socket (com⁩/⁨nice⁩/⁨hki⁩/protocol⁩/ConnectionsManager.java):
private Socket _openSocket(String host, int port) throws Exception {
        Socket socket = null;
        for (Network nw : this.cm.getAllNetworks()) {
            int type = this.cm.getNetworkInfo(nw).getType();
            if (type == 1 || type == 0 || type == 4 || type == 5) {
                socket = nw.getSocketFactory().createSocket();
                break;
            }
        }
        if (socket == null) {
            throw new Exception("No network available!");
        }

        socket.setReuseAddress(true);
        socket.connect(new InetSocketAddress(host, port), 5000);

        TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllManager()};
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new SecureRandom());
        socket = sslContext.getSocketFactory().createSocket(socket, host, port, true);
        ((SSLSocket) socket).startHandshake();

        return socket;
    }
  1. Now we can send requests. Most important function is buildCommand (com/nice/hki/protocol/commands/CommandHandler.java):
private synchronized Command buildCommand(CommandType commandType, String target, String protocol, String body) throws Exception {
        String commandID;
        String startRequest;
        commandID = generateCommandID(target);
        startRequest = buildStartRequest(commandID, commandType, Plugin.APP_UDID, target, protocol);
        return new Command(commandID, target, commandType, protocol, startRequest + body + (isSignNeeded(commandType, protocol) ? buildSign(target, commandType, startRequest + body) : "") + buildEndRequest());
    }

Generating CommandID, it depends on session ID:

private static int cmdSequence = 1;
...
private String generateCommandID(String target) {
        Session session = ConnectionsManager.getInstance().getActiveConnection(target).getSession(target);
        Integer sessionID = Integer.valueOf(0);
        if (!(session == null || session.getSessionID() == null)) {
            sessionID = session.getSessionID();
        }
        int i = cmdSequence;
        cmdSequence = i + 1;
        return String.valueOf((i << 8) | (sessionID.intValue() & 255));
    }

Function build header and footer:

private String buildStartRequest(String commandID, CommandType commandType, String source, String target, String protocol) {
        return String.format("<Request id=\"%s\" source=\"%s\" target=\"%s\" gw=\"gwID\" protocolType=\"%s\" protocolVersion=\"%s\" type=\"%s\">\r\n", new Object[]{commandID, source, target, protocol, "1.0", commandType});
    }

private String buildEndRequest() {
        return "</Request>\r\n";
}

Checks if a signature is needed:

private boolean isSignNeeded(CommandType commandType, String protocol) {
        return !protocol.equals("CLOUD") && commandType.needsSign();
    }

And signature building:

private String buildSign(String target, CommandType commandType, String xmlCommand) throws Exception {
        Session session = ConnectionsManager.getInstance().getActiveSession(target);
        if (session == null || session.getSessionPassword() == null) {
            throw new Exception(Constants.ERROR_NO_SESSION);
        }
        return String.format("<Sign>%s</Sign>", new Object[]{session.signRequest(xmlCommand)});
    }

signRequest we can find in Session.java (com/nice/hki/authentication/Session.java):

public String signRequest(String xmlRequest) throws Exception {
        try {
            byte[] msgHash = Utils.sha256(xmlRequest.getBytes());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            baos.write(msgHash);
            baos.write(this.sessionPassword);

            byte[] sign = Utils.sha256(msgHash, this.sessionPassword);

            return Utils.base64Encode(sign);
        } catch (Exception e) {
            Log.e(Constants.LOG_TAG, "Error signing request. " + e.getMessage());
            return "";
        }
    }

Utils.sha256 we can find in com/nice/hki/Utils.java:

public static byte[] sha256(byte[]... values) throws Exception {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        for (byte[] data : values) {
            digest.update(data);
        }
        return digest.digest();
    }

this.sessionPassword generated in setServerChallenge function in Session.java:

byte[] serverChallengeArr = Utils.hexStringToByteArray(serverChallenge);
byte[] clientChallengeArr = Utils.hexStringToByteArray(this.clientChallenge);
this.sessionPassword = Utils.sha256(this.pairingPassword, Utils.invertArray(serverChallengeArr), Utils.invertArray(clientChallengeArr));

this.pairingPassword is base64 decoded pwd from pair request. serverChallenge is SC from my previous message. this.clientChallenge is CC and randomly generated:

private String clientChallenge = Utils.intToHexString(new Random().nextInt());

Utils.invertArray, Utils.hexStringToByteArray and Utils.intToHexString:

public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[(len / 2)];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }
public static byte[] invertArray(byte[] data) {
        byte[] result = new byte[data.length];
        int i = data.length - 1;
        int c = 0;
        while (i >= 0) {
            int c2 = c + 1;
            result[c] = data[i];
            i--;
            c = c2;
        }
        return result;
    }
public static String intToHexString(int value) {
        return String.format("%08x", new Object[]{Integer.valueOf(value)}).toUpperCase();
    }

Before sending result from buildCommand function to socket we need to do that:

result = ('\u0002' + buildCommand.result + '\u0003').getBytes("UTF-8"));

Thats all what we need to connect and sending request.

1 Like

And here my program on Python: nice.txt (7.9 KB) (rename it to nice.py)

It can be used like that:

from nice import NiceGate

### Nice Settings
# IT4WIFI IP address
nice_host = '192.168.1.12'
# IT4WIFI Mac address
nice_target = "00:0B:6C:11:22:33"
# Name for client device
nice_source = "python"
# Username for client
nice_username = "Python"
# User description
nice_descr = "script"
# Password from PAIR message
nice_pwd = "JDAMNOYgdnudTiF1XV7sWkQyrQxAYH9Yi6ZqbrrOzAA="

nice = NiceGate(nice_host, nice_target, nice_source, nice_username, nice_descr, nice_pwd)
nice.debug = True
nice.connect()
status = nice.status()
change = nice.change('open')

For the first time you need to run connect command with ‘pair’ key to create a user:

nice.connect('pair')

Copy pwd from output and write it to nice_pwd variable.
Loop function can be used for daemon and mqtt implementation. But my program have a troubles with it: I cant catch event response and it breaks response in next message. For example, I send CHANGE command:

<Request id="4353" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
<Devices><Device id="1">
<Services><DoorAction>open</DoorAction>
</Services ></Device></Devices><Sign>PhAkG7OPsG0+GmIYXHBSXXZGCbSGsrUcJlNdi3fLmuE=</Sign></Request>

My program catching only response without Events:

<Response  id="4353" source="00:0B:6C:11:22:33" target="58c91234ea321d34" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
</Response>

And if I send another command after CHANGE, STATUS for example, it will get an Event as response from previous change:

<Request id="2049" source="58c91234ea321d34" target="00:0B:6C:11:22:33" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="STATUS">

Answer:

<Event id="41" source="00:0B:6C:11:22:33" target="58c91234ea321d34" protocolType="NHK" protocolVersion="1.0" type="CHANGE">
<Timestamp>2018-04-21T23:25:56</Timestamp>
<Devices>
<Device id="1">
<Properties>
<DoorStatus>opening</DoorStatus>
<Obstruct>0</Obstruct>
</Properties>
</Device>
</Devices>
</Event>

Maybe somebody can fix it.

1 Like

Hello! First let me thank you for a great work! Your python script helped me very well. :+1: :+1: :+1:
Second, Im sorry for my bad English :sleepy:

We can send more commands to IT4WIFI accessory:

  1. INFO request may give us many ibformation about accessory and gate motor. Just add simple code to script:
def info(self):
    info = self.buildMessage('INFO', '')
    return info

It will send request like this:

 <Request id="770" source="python" target="00:0B:6C:48:C8:49" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="INFO">
 <Sign>7vv9d9EWpu3gAicHLEZx8Vttz9E6K976zDBIhU86L3c=</Sign></Request>

And we receive answer from IT4WIFI:

#####BEGIN####
<Response  id="770" source="00:0B:6C:48:C8:49" target="python" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="INFO">
<Interface>
<Date>2020-09-08T13:24:24Z</Date>
<Zone>+4:00</Zone>
<DST>+0:00</DST>
<VersionHW>SB575B-R10.0</VersionHW>
<VersionFW>0.3.14</VersionFW>
<Manuf>Nice Spa</Manuf>
<Prod>IT4WIFI</Prod>
<SerialNr>0Q011906004U725CE</SerialNr>
<Settings>
<Name type="string" perm="r,w"/>
<Location type="string" perm="r,w"/>
<DateTime type="string" perm="w"/>
<ZoneTime type="string" perm="w"/>
<DST type="string" perm="w"/>
</Settings>
<Commands>
<Reboot type="bool" perm="w">
</Reboot>
<SettingReset type="bool" perm="w">
</SettingReset>
<FactoryReset type="bool" perm="w">
</FactoryReset>
<FWupgrade type="bool" perm="w">
</FWupgrade>
</Commands>
<Events hyst_len="32">
<LastEvent type="int" perm="r">
</LastEvent>
</Events>
</Interface>
<Devices>
<Device id="1">
<Type>sliding</Type>
<Manuf>NICE</Manuf>
<Prod>ROBUSR10</Prod>
<Desc>NewRobus</Desc>
<VersionHW>604ar00</VersionHW>
<VersionFW>FG01b</VersionFW>
<SerialNr>80BB1709</SerialNr>
<Services>
<T4Action type="string" perm="w">
</T4Action>
<DoorAction type="string" values="open, stop, close" perm="w">
</DoorAction>
</Services>
<Properties>
<DoorStatus type="string" values="open, closed, opening, closing, stopped" perm="r">
</DoorStatus>
<Obstruct type="bool" perm="r
####END####

How we can see, answer not full. Its truncated at end. Mistake is in recvall function. IT4WIFI can send data in chunks less than the buffer size. First time I made some like this in recvall:

def recvall(self):
    BUFF_SIZE = 512
    data = b''
    while True:
        part = self.serv.recv(BUFF_SIZE)
        data += part
        if re.search(b'\x02', data):
            print('STX in answer founded ..... ')
            data = data[1:]
        if re.search(b'\x03', data):
            print('ETX in answer founded. Full message received.....')
            data = data[:-1]
            print(data)
            print('Answer lenght = ' + str(len(data)) + ' bytes')
            break
    return data

Now we receive full message from IT4WIFI and its truncated from STX and ETX symbols. Its not very good method, but its usefull for first time.
answer from gates:

#####BEGIN####
<Response  id="770" source="00:0B:6C:48:C8:49" target="python" gw="gwID" protocolType="NHK" protocolVersion="1.0" type="INFO">
<Interface>
<Date>2020-09-08T13:40:45Z</Date>
<Zone>+4:00</Zone>
<DST>+0:00</DST>
<VersionHW>SB575B-R10.0</VersionHW>
<VersionFW>0.3.14</VersionFW>
<Manuf>Nice Spa</Manuf>
<Prod>IT4WIFI</Prod>
<SerialNr>0Q011906004U725CE</SerialNr>
<Settings>
<Name type="string" perm="r,w"/>
<Location type="string" perm="r,w"/>
<DateTime type="string" perm="w"/>
<ZoneTime type="string" perm="w"/>
<DST type="string" perm="w"/>
</Settings>
<Commands>
<Reboot type="bool" perm="w">
</Reboot>
<SettingReset type="bool" perm="w">
</SettingReset>
<FactoryReset type="bool" perm="w">
</FactoryReset>
<FWupgrade type="bool" perm="w">
</FWupgrade>
</Commands>
<Events hyst_len="32">
<LastEvent type="int" perm="r">
</LastEvent>
</Events>
</Interface>
<Devices>
<Device id="1">
<Type>sliding</Type>
<Manuf>NICE</Manuf>
<Prod>ROBUSR10</Prod>
<Desc>NewRobus</Desc>
<VersionHW>604ar00</VersionHW>
<VersionFW>FG01b</VersionFW>
<SerialNr>80BB1709</SerialNr>
<Services>
<T4Action type="string" perm="w">
</T4Action>
<DoorAction type="string" values="open, stop, close" perm="w">
</DoorAction>
</Services>
<Properties>
<DoorStatus type="string" values="open, closed, opening, closing, stopped" perm="r">
</DoorStatus>
<Obstruct type="bool" perm="r">
</Obstruct>
<T4_allowed type="hex" values="0000003E" perm="r"/>
</Properties>
<Events hyst_len="32">
<LastEvent type="int" perm="r">
</LastEvent>
</Events>
<Scheduled>
<Service max_id="10" type="string" action="string"/>
</Scheduled>
</Device>
</Devices>
</Response>
####END####
  1. Further I find that gates can react on T4 commands (its T4Bus commands from Nice). For example my gates have Partial Opening function. If I press second button on transmitter, gate will slide one meter from closed position and stopped. To do this via script we need add function like this:
    # partial opening via T4 commands
    def t4(self, command):
        change = self.buildMessage('CHANGE', '<Devices><Device id="1">\n<Services><T4Action>{}</T4Action>\n</Services ></Device></Devices>'.format(command))

Full list of all possible T4 commands (some of commands may be not supported by different motors. See your motor specification.)

#    1 "MDAx", "Step by Step"
#    2 "MDAy", "Stop"
#    3 "MDAz", "Open"
#    4 "MDA0", "Close"
#    5 "MDA1", "Partial opening 1"
#    6 "MDA2", "Partial opening 2"
#    7 "MDA3", "Partial opening 3"
#    11 "MDBi", "Apartment Step by Step"
#    12 "MDBj", "Step by Step high priority"
#    13 "MDBk", "Open + Block"
#    14 "MDBl", "Close + Block"
#    15 "MDBm", "Block"
#    16 "MDEw", "Release"
#    17 "MDEx", "Courtesy light timer on"
#    18 "MDEy", "Courtesy light On-Off"
#    19 "MDEz", "Step by Step master door"
#    20 "MDE0", "Open master door"
#    21 "MDE1", "Close master door"),
#    22 "MDE2", "Step by Step slave door"
#    23 "MDE3", "Open slave door"
#    24 "MDE4", "Close slave door"
#    25 "MDE5", "Release + Open"
#    26 "MDFh", "Release + Close"

To use it we need call nice.t4() with str parameter like this:

nice.t4('MDA1')

In next post I will try tell about catching EVENT responses and some troubles with it.

1 Like

Understanding logic of gate can help us write script.
When gate is in hold state (its closed, opened or stopped) - no events accure.
When some device stimulate gates to change its state, gates produce some events of 2 types:
First type of event - “immediate event”. Iits occur “next-to-command”. I.e. if we press button on transmitter, or send command via app or script, gates immediately generate event and start changing self state (opening, closing etc).
Second type of events - “procrastinated events”. They occur past some time after command, when gates reach stabile state after changing state after command.
Its logic like this:
Command sended to gates.
If gates response command and command valid, gates send back event message and start changing self state.


When gates reach stabile its send second mesage.
Original script from @morchee collect data from socket via syncronized method.
But when we read one message from socket buffer (usually its immediately-event-message), since some time accessory send another message. We need to read it. And if we want monitoring state of gates via script, we need to listen socket.
Trouble in originally script is it sync methodology.
If we will use originall recvall in loop, - on part = self.serv.recv(BUFF_SIZE) it will be freeze until new data in income buffer of socket will arrive. At night it can be some hours. And connection will broke by accessory side.
I am use pseudo-async method via

        while -some-expression-:
            ready, w, e = select.select([self.serv],[],[],0.5)

and controlling pending data via self.serv.pending()

At this time not understanding how recieve log of events from gate motor(accessory). And Im still working.

1 Like

Anybody tried to sniff traffic between IT4WIFI and mainboard? I believe it can be serial data, so it would be possible to get rid of IT4WIFI and replace it by Esp8266 as I did with panasonic air-water heat pump. Decoded all communication between wifi module and heat pump.

hi, from were you got these BUS T4 commands ? Maybe you have more info about this protocol ?

Hi. Im got it from reversing Android apk.

package com.nice.hki.model;

import com.adobe.phonegap.push.PushConstants;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;

public enum T4Command implements Jsonable {
    MDAx(1, "MDAx", "Passo passo"),
    MDAy(2, "MDAy", "Stop"),
    MDAz(3, "MDAz", "Apre"),
    MDA0(4, "MDA0", "Chiude"),
    MDA1(5, "MDA1", "Apre parziale 1"),
    MDA2(6, "MDA2", "Apre parziale 2"),
    MDA3(7, "MDA3", "Apre parziale 3"),
    MDBi(11, "MDBi", "Passo passo condominiale"),
    MDBj(12, "MDBj", "Passo passo alta priorità"),
    MDBk(13, "MDBk", "Apre + Blocca"),
    MDBl(14, "MDBl", "Chiudi + Blocca"),
    MDBm(15, "MDBm", "Blocca"),
    MDEw(16, "MDEw", "Sblocca"),
    MDEx(17, "MDEx", "On timer luce di cortesia"),
    MDEy(18, "MDEy", "On-Off luce di cortesia"),
    MDEz(19, "MDEz", "Passo passo anta master"),
    MDE0(20, "MDE0", "Apre anta master"),
    MDE1(21, "MDE1", "Chiude anta master"),
    MDE2(22, "MDE2", "Passo passo anta slave"),
    MDE3(23, "MDE3", "Apre anta slave"),
    MDE4(24, "MDE4", "Chiude anta slave"),
    MDE5(25, "MDE5", "Sblocca + Apre"),
    MDFh(26, "MDFh", "Sblocca + Chiude");
    
    private int bitPosition;
    private String code;
    private String description;

    public int getBitPosition() {
        return this.bitPosition;
    }

    public String getCode() {
        return this.code;
    }

    public String getDescription() {
        return this.description;
    }

    private T4Command(int bitPosition2, String code2, String description2) {
        this.bitPosition = bitPosition2;
        this.code = code2;
        this.description = description2;
    }

    public static T4Command fromCode(String commandCode) {
        T4Command[] values;
        for (T4Command command : values()) {
            if (command.getCode().equalsIgnoreCase(commandCode)) {
                return command;
            }
        }
        throw new IllegalArgumentException("Unknown T4 command code (" + commandCode + ")");
    }

    public static List<T4Command> fromBitmask(int bitmask) {
        T4Command[] values;
        List<T4Command> result = new ArrayList<>();
        for (T4Command command : values()) {
            if (((1 << command.getBitPosition()) & bitmask) != 0) {
                result.add(command);
            }
        }
        return result;
    }

    public static int getBitmaskFromCommandCodes(String[] commandCodes) {
        int bitmask = 0;
        for (String commandCode : commandCodes) {
            if (commandCode.trim().length() != 0) {
                bitmask |= 1 << fromCode(commandCode.trim()).getBitPosition();
            }
        }
        return bitmask;
    }

    public JSONObject toJSON() throws JSONException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", this.code);
        jsonObject.put(PushConstants.CHANNEL_DESCRIPTION, this.description);
        return jsonObject;
    }
}

No, Im not, because I have IT4WiFi. But I think its some like rs232. Be carefull with Vcc.

nice, it helped me to figure out byte for control :slight_smile:
and all self state events + stabile event messages after command I assume dont have T4 Bus format here as IT4WiFi “translates” in normal language ?

Im sorry, my english not so good, may be i understend som your questions wrong. Please ask me simply.

is there any reply events after command have been send in T4 command format ?