Send a message to the Chromecast.

def send_message(self, destination_id, namespace, data,
                     inc_session_id=False, callback_function=False,
                     no_add_request_id=False, force=False):
        """ Send a message to the Chromecast. """

        # namespace is a string containing namespace
        # data is a dict that will be converted to json
        # wait_for_response only works if we have a request id

        # If channel is not open yet, connect to it.
        self._ensure_channel_connected(destination_id)

        request_id = None
        if not no_add_request_id:
            request_id = self._gen_request_id()
            data[REQUEST_ID] = request_id

        if inc_session_id:
            data[SESSION_ID] = self.session_id

        # pylint: disable=no-member
        msg = cast_channel_pb2.CastMessage()

        msg.protocol_version = msg.CASTV2_1_0
        msg.source_id = self.source_id
        msg.destination_id = destination_id
        msg.payload_type = cast_channel_pb2.CastMessage.STRING
        msg.namespace = namespace
        msg.payload_utf8 = _json_to_payload(data)

        # prepend message with Big-Endian 4 byte payload size
        be_size = pack(">I", msg.ByteSize())

        # Log all messages except heartbeat
        if msg.namespace != NS_HEARTBEAT:
            self.logger.debug("[%s:%s] Sending: %s",
                              self.fn or self.host, self.port,
                              _message_to_string(msg, data))

        if not force and self.stop.is_set():
            raise PyChromecastStopped("Socket client's thread is stopped.")
        if not self.connecting and not self._force_recon:
            try:
                if not no_add_request_id and callback_function:
                    self._request_callbacks[request_id] = {
                        'event': threading.Event(),
                        'response': None,
                        'function': callback_function,
                    }
                self.socket.sendall(be_size + msg.SerializeToString())
            except socket.error:
                self._request_callbacks.pop(request_id, None)
                self._force_recon = True
                self.logger.info('[%s:%s] Error writing to socket.',
                                 self.fn or self.host, self.port)
        else:
            raise NotConnected("Chromecast " + self.host + ":" +
                               str(self.port) + " is connecting...")