async def issue_cmd(self, cmd, value, retry=3):
        """
        Issue a command, then await and return the return value.

        This method is a coroutine
        """
        async with self._cmd_lock:
            if not self.connected:
                _LOGGER.debug(
                    "Serial transport closed, not sending command %s", cmd)
                return
            while not self._cmdq.empty():
                _LOGGER.debug("Clearing leftover message from command queue:"
                              " %s", await self._cmdq.get())
            _LOGGER.debug("Sending command: %s with value %s", cmd, value)
            self.transport.write(
                '{}={}\r\n'.format(cmd, value).encode('ascii'))
            if cmd == OTGW_CMD_REPORT:
                expect = r'^{}:\s*([A-Z]{{2}}|{}=[^$]+)$'.format(cmd, value)
            else:
                expect = r'^{}:\s*([^$]+)$'.format(cmd)

            async def send_again(err):
                """Resend the command."""
                nonlocal retry
                _LOGGER.warning("Command %s failed with %s, retrying...", cmd,
                                err)
                retry -= 1
                self.transport.write(
                    '{}={}\r\n'.format(cmd, value).encode('ascii'))

            async def process(msg):
                """Process a possible response."""
                _LOGGER.debug("Got possible response for command %s: %s", cmd,
                              msg)
                if msg in OTGW_ERRS:
                    # Some errors appear by themselves on one line.
                    if retry == 0:
                        raise OTGW_ERRS[msg]
                    await send_again(msg)
                    return
                if cmd == OTGW_CMD_MODE and value == 'R':
                    # Device was reset, msg contains build info
                    while not re.match(
                            r'OpenTherm Gateway \d+\.\d+\.\d+', msg):
                        msg = await self._cmdq.get()
                    return True
                match = re.match(expect, msg)
                if match:
                    if match.group(1) in OTGW_ERRS:
                        # Some errors are considered a response.
                        if retry == 0:
                            raise OTGW_ERRS[match.group(1)]
                        await send_again(msg)
                        return
                    ret = match.group(1)
                    if cmd == OTGW_CMD_SUMMARY and ret == '1':
                        # Expects a second line
                        part2 = await self._cmdq.get()
                        ret = [ret, part2]
                    return ret
                if re.match(r'Error 0[1-4]', msg):
                    _LOGGER.warning("Received %s. If this happens during a "
                                    "reset of the gateway it can be safely "
                                    "ignored.", msg)
                    return
                _LOGGER.warning("Unknown message in command queue: %s", msg)
                await send_again(msg)

            while True:
                msg = await self._cmdq.get()
                ret = await process(msg)
                if ret is not None:
                    return ret
Issue a command, then await and return the return value.

        This method is a coroutine