sendMessage(msg, options = {}) {
        return new Promise((resolve, reject) => {

            // If a message is a reply to a request from the robot, it will already have an ID
            if (msg !== null && msg !== undefined && !msg["id"]) {
                /*
                    This behaves differently, because the local socket might have other connections exhausting
                    msgIds, which is why we need to find a way to stay on top.

                    This problem should not exist with the cloud socket, allowing us to just count up
                    on each new message
                 */
                if (this.isCloudSocket) {
                    if (this.nextId > MAX_INT32) {
                        this.nextId = 1;
                    }

                    msg["id"] = this.nextId++;
                } else {
                    /*
                        Unexpectedly, it is not required for the next msgId to be larger than the previous one
                        It just needs to be different
                     */
                    msg["id"] = MiioSocket.calculateMsgId(new Date());
                }
            }

            /*
                If a message has a result or error property, it is a response to a request from the robot,
                meaning that we should not add it to our pending requests
             */
            if (msg !== null && msg !== undefined && !msg["result"] && !msg["error"]) {
                const msgId = msg["id"];

                this.pendingRequests[msgId] = {
                    resolve: resolve,
                    reject: reject,
                    method: msg["method"],
                    onTimeoutCallback: () => {
                        Logger.debug(`${this.name} request ${msgId} ${msg["method"]} timed out`);
                        delete this.pendingRequests[msgId];

                        if (this.isCloudSocket && this.connected === true) {
                            Logger.info("Cloud message timed out. Assuming that we're not connected anymore");

                            this.connected = false;
                        }


                        reject(new MiioTimeoutError(msg));
                    }
                };

                this.pendingRequests[msgId].timeout_id = setTimeout(
                    () => {
                        this.pendingRequests[msgId].onTimeoutCallback();
                    },
                    options.timeout ?? this.timeout
                );
            }

            const packet = this.codec.encodeOutgoingMiioPacket(msg, this.deviceId);

            Logger.debug(">>> " + this.name + ":", msg);
            this.socket.send(packet, 0, packet.length, this.rinfo.port, this.rinfo.address);
        });
    }
Used for both sending commands or responses.

@param {object?} msg JSON object to send to remote
@param {object} options
@param {number=} options.timeout timeout in milliseconds, in case of timeout returns a failed promise with err = 'timeout'
@returns {Promise<object>}