public enum DisplayDirection {
	NORMAL, ROTATION_180;
} public class DrawingColors {
	private final Color foreground;

	private final Color background;

	public DrawingColors(Color foreground, Color background) {
		this.foreground = foreground;
		this.background = background;
	}

	public Color getForeground() {
		return foreground;
	}

	public Color getBackground() {
		return background;
	}
} public class EPaperException extends Exception {
	private static final long serialVersionUID = 1L;

	public EPaperException() {
		super();
	}

	public EPaperException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}

	public EPaperException(String message, Throwable cause) {
		super(message, cause);
	}

	public EPaperException(String message) {
		super(message);
	}

	public EPaperException(Throwable cause) {
		super(cause);
	}
} public class EPaperResponseException extends EPaperException {
	private static final long serialVersionUID = 1L;

	public EPaperResponseException(byte[] response) {
		super(getMessage(response));
	}

	public EPaperResponseException(byte[] response, Exception cause) {
		super(getMessage(response), cause);
	}

	public EPaperResponseException(String message) {
		super(message);
	}

	public EPaperResponseException(String message, Exception cause) {
		super(message, cause);
	}

	private static String getMessage(byte[] response) {
		return new String(response, StandardCharsets.US_ASCII);
	}
} public enum FontSize {
	DOTS_32(32, 1), DOTS_48(48, 1), DOTS_64(64, 1);

	private final int height;

	private final int letterSpacing;

	private FontSize(int height, int letterSpacing) {
		this.height = height;
		this.letterSpacing = letterSpacing;
	}

	public int getHeight() {
		return height;
	}

	public int getLetterSpacing() {
		return letterSpacing;
	}
} public abstract class AbstractEPaperClient implements EPaperClient, CellContentDrawer {
	@Override
	public synchronized void drawImage(int x, int y, InputStream inputStream) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x, y);
		Map<Color, List<Point>> colorPointsMap = Colors.getColorPointsMap(inputStream);
		for (Color color : Color.values()) {
			setDrawingColors(new DrawingColors(color, Color.WHITE));
			for (Point point : colorPointsMap.get(color)) {
				drawPoint(x + point.x, y + point.y);
			}
		}
	}

	@Override
	public synchronized void drawPadded(int x0, int y0, int x1, int y1, int leftPadding, int rightPadding,
			int topPadding, int bottomPadding, CellContent cellContent) throws IOException, EPaperException {
		int topLeftX = Coordinates.getTopLeftX(x0, x1);
		int topLeftY = Coordinates.getTopLeftY(y0, y1);
		int bottomRightX = Coordinates.getBottomRightX(x0, x1);
		int bottomRightY = Coordinates.getBottomRightY(y0, y1);
		cellContent.draw(topLeftX + leftPadding, topLeftY + topPadding, bottomRightX - rightPadding,
				bottomRightY - bottomPadding, this);
	}

	@Override
	public synchronized void drawText(int x0, int y0, int x1, int y1, Color textColor, Color backgroundColor,
			FontSize fontSize, int lineSpacing, HorizontalAlignment horizontalAlignment,
			VerticalAlignment verticalAlignment, String text) throws IOException, EPaperException {
		setDrawingColors(new DrawingColors(textColor, backgroundColor));
		displayText(x0, y0, x1, y1, fontSize, lineSpacing, horizontalAlignment, verticalAlignment, text);
	}

	@Override
	public synchronized void drawTable(int x0, int y0, int x1, int y1, Table table)
			throws IOException, EPaperException {
		Coordinates.validateCoordinates(x0, y0);
		Coordinates.validateCoordinates(x1, y1);
		int topLeftX = Coordinates.getTopLeftX(x0, x1);
		int topLeftY = Coordinates.getTopLeftY(y0, y1);
		int width = Coordinates.getBottomRightX(x0, x1) - topLeftX + 1;
		int height = Coordinates.getBottomRightY(y0, y1) - topLeftY + 1;
		Tables.validateTable(width, height, table);
		DrawableTable drawableTable = Tables.getDrawableTable(width, height, topLeftX, topLeftY, table);
		for (DrawableCell drawableCell : drawableTable.getDrawableCells()) {
			if (drawableCell.getBackgroundColor() != null) {
				setDrawingColors(
						new DrawingColors(drawableCell.getBackgroundColor(), drawableCell.getBackgroundColor()));
				fillRectangle(drawableCell.getX0(), drawableCell.getY0(), drawableCell.getX1(), drawableCell.getY1());
			}
			for (CellContent cellContent : drawableCell.getCellContents()) {
				cellContent.draw(drawableCell.getX0(), drawableCell.getY0(), drawableCell.getX1(), drawableCell.getY1(),
						this);
			}
		}
		for (Entry<Color, List<DrawableBorder>> colorDrawableBorderListEntry : drawableTable.getDrawableBorders()
				.stream().collect(Collectors.groupingBy(DrawableBorder::getColor)).entrySet()) {
			setDrawingColors(
					new DrawingColors(colorDrawableBorderListEntry.getKey(), colorDrawableBorderListEntry.getKey()));
			for (DrawableBorder drawableBorder : colorDrawableBorderListEntry.getValue()) {
				fillRectangle(drawableBorder.getX0(), drawableBorder.getY0(), drawableBorder.getX1(),
						drawableBorder.getY1());
			}
		}
	}

	@Override
	public synchronized void displayText(int x0, int y0, int x1, int y1, FontSize fontSize, int lineSpacing,
			HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, String text)
			throws IOException, EPaperException {
		if (text == null || text.isEmpty()) {
			return;
		}
		Coordinates.validateCoordinates(x0, y0);
		Coordinates.validateCoordinates(x1, y1);
		Lines.validateFontSize(fontSize);
		Lines.validateSpacing(lineSpacing);
		Lines.validateHorizontalAlignment(horizontalAlignment);
		Lines.validateVerticalAlignment(verticalAlignment);
		Lines.validateAdvancedText(text);
		int topLeftX = Coordinates.getTopLeftX(x0, x1);
		int topLeftY = Coordinates.getTopLeftY(y0, y1);
		int width = Coordinates.getBottomRightX(x0, x1) - topLeftX + 1;
		int height = Coordinates.getBottomRightY(y0, y1) - topLeftY + 1;
		List<Line> lines = Lines.getLines(width, height, topLeftX, topLeftY, fontSize, lineSpacing, horizontalAlignment,
				verticalAlignment, text);
		setFontSize(fontSize);
		for (Line line : lines) {
			displayText(line.getX(), line.getY(), line.getText());
		}
	}
} public class Colors {
	public static Color fromRgb(int rgb) {
		java.awt.Color color = new java.awt.Color(rgb);
		return Color.values()[(int) Math
				.floor((0.21 * color.getRed() + 0.72 * color.getGreen() + 0.07 * color.getBlue()) / 64d)];
	}

	public static Map<Color, List<Point>> getColorPointsMap(InputStream inputStream) throws IOException {
		BufferedImage image = ImageIO.read(inputStream);
		Map<Color, List<Point>> points = new HashMap<>();
		for (Color color : Color.values()) {
			points.put(color, new ArrayList<>());
		}
		for (int y = 0; y < image.getHeight(); y++) {
			for (int x = 0; x < image.getWidth(); x++) {
				points.get(Colors.fromRgb(image.getRGB(x, y))).add(new Point(x, y));
			}
		}
		return points;
	}

	public static void validateDrawingColors(DrawingColors drawingColors) throws EPaperValidationException {
		if (drawingColors == null) {
			throw new EPaperValidationException("Drawing colors null");
		}
		if (drawingColors.getBackground() == null) {
			throw new EPaperValidationException("Background drawing color null");
		}
		if (drawingColors.getForeground() == null) {
			throw new EPaperValidationException("Foreground drawing color null");
		}
	}
} public class Coordinates {
	public static int getTopLeftX(int x0, int x1) {
		return Math.min(x0, x1);
	}

	public static int getTopLeftY(int y0, int y1) {
		return Math.min(y0, y1);
	}

	public static int getBottomRightX(int x0, int x1) {
		return Math.max(x0, x1);
	}

	public static int getBottomRightY(int y0, int y1) {
		return Math.max(y0, y1);
	}

	public static void validateCoordinates(int x, int y) throws EPaperValidationException {
		if (x < 0 || x >= EPaperClient.WIDTH) {
			throw new EPaperValidationException("Invalid x: " + x);
		}
		if (y < 0 || y >= EPaperClient.HEIGHT) {
			throw new EPaperValidationException("Invalid y: " + y);
		}
	}

	public static void validateDisplayDirection(DisplayDirection displayDirection) throws EPaperValidationException {
		if (displayDirection == null) {
			throw new EPaperValidationException("Empty display direction");
		}
	}
} public class Lines {
	public static final char NO_BREAK_SPACE = '\u00A0';

	public static final String LINE_FEED = "\n";

	private static final Pattern VALID_TEXT_PATTERN = Pattern.compile("[[\\p{Print}&&\\p{ASCII}]]+");

	private static final Pattern VALID_ADVANCED_TEXT_PATTERN = Pattern.compile("[[\\p{Print}&&\\p{ASCII}]\u00A0\n]+");

	public static void validateFontSize(FontSize fontSize) throws EPaperValidationException {
		if (fontSize == null) {
			throw new EPaperValidationException("Empty font size");
		}
	}

	public static void validateSpacing(int spacing) throws EPaperValidationException {
		if (spacing < 0) {
			throw new EPaperValidationException("Negative spacing");
		}
	}

	public static void validateHorizontalAlignment(HorizontalAlignment horizontalAlignment)
			throws EPaperValidationException {
		if (horizontalAlignment == null) {
			throw new EPaperValidationException("Empty horizontal alignment");
		}
	}

	public static void validateVerticalAlignment(VerticalAlignment verticalAlignment) throws EPaperValidationException {
		if (verticalAlignment == null) {
			throw new EPaperValidationException("Empty vertical alignment");
		}
	}

	public static void validateText(String text) throws EPaperValidationException {
		Matcher matcher = VALID_TEXT_PATTERN.matcher(text);
		if (!matcher.matches()) {
			throw new EPaperValidationException("Invalid character");
		}
	}

	public static void validateAdvancedText(String text) throws EPaperValidationException {
		Matcher matcher = VALID_ADVANCED_TEXT_PATTERN.matcher(text);
		if (!matcher.matches()) {
			throw new EPaperValidationException("Invalid character");
		}
	}

	public static int getMaxLines(int height, FontSize fontSize, int spacing) throws EPaperValidationException {
		int maxLines = (height + spacing) / (fontSize.getHeight() + spacing);
		if (maxLines < 1) {
			throw new EPaperValidationException("Not enough height");
		}
		return maxLines;
	}

	public static List<String> getWords(String text) {
		List<String> words = Arrays.asList(text.split(" ")).stream()
				.map(w -> w.replace(NO_BREAK_SPACE, PrintableCharacter.SPACE.getCharacter()))
				.collect(Collectors.toList());
		for (int i = 0; i < words.size(); i++) {
			String word = words.get(i);
			String[] newWords = word.split(LINE_FEED, -1);
			if (newWords.length > 1) {
				words.remove(i);
				for (int j = newWords.length - 1; j > -1; j--) {
					String newWord = newWords[j];
					if (!newWord.isEmpty()) {
						words.add(i, newWord);
					}
					if (j > 0) {
						words.add(i, LINE_FEED);
					}
				}
			}
		}
		return words;
	}

	public static List<Line> constructLines(int width, int height, FontSize fontSize, int spacing, String text)
			throws EPaperValidationException {
		int maxLines = Lines.getMaxLines(height, fontSize, spacing);
		List<String> words = Lines.getWords(text);
		List<Line> lines = new ArrayList<>();
		if (!words.isEmpty()) {
			Line line = new Line(width, fontSize);
			lines.add(line);
			for (String word : words) {
				boolean newLine = word.equals(LINE_FEED);
				if (newLine || !line.addWord(word)) {
					if (lines.size() == maxLines) {
						break;
					} else {
						line = new Line(width, fontSize);
						lines.add(line);
					}
					if (!newLine) {
						line.addWord(word);
					}
				}
			}
		}
		return lines;
	}

	public static void alignLines(int width, int height, int topLeftX, int topLeftY, FontSize fontSize, int spacing,
			HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, List<Line> lines) {
		int linesHeight = lines.size() * (fontSize.getHeight() + spacing) - spacing;
		int y = topLeftY;
		switch (verticalAlignment) {
		case BOTTOM:
			y += height - linesHeight;
			break;
		case CENTER:
			y += (height - linesHeight) / 2;
			break;
		case TOP:
			break;
		default:
			throw new IllegalStateException("Invalid vertical alignment");
		}
		for (Line line : lines) {
			switch (horizontalAlignment) {
			case CENTER:
				line.setX(topLeftX + (width - line.getWidth()) / 2);
				break;
			case LEFT:
				line.setX(topLeftX);
				break;
			case RIGHT:
				line.setX(topLeftX + width - line.getWidth());
				break;
			default:
				throw new IllegalStateException("Invalid horizontal alignment");
			}
			line.setY(y);
			y += fontSize.getHeight() + spacing;
		}
	}

	public static List<Line> getLines(int width, int height, int topLeftX, int topLeftY, FontSize fontSize, int spacing,
			HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, String text)
			throws EPaperValidationException {
		List<Line> lines = constructLines(width, height, fontSize, spacing, text);
		alignLines(width, height, topLeftX, topLeftY, fontSize, spacing, horizontalAlignment, verticalAlignment, lines);
		return lines;
	}

	public static FontSize getFontSize(byte[] response) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append((char) response[0]);
		return FontSize.values()[Integer.parseInt(stringBuffer.toString()) - 1];
	}
} public class ClearCommand extends Command {
	public ClearCommand() {
		super(0x2E);
	}
} public class Command {
	private static final byte FRAME_HEADER = (byte) 0xA5;

	private static final byte[] FRAME_END = new byte[] { (byte) 0xCC, (byte) 0x33, (byte) 0xC3, (byte) 0x3C };

	private final short length;

	private final byte[] frame;

	public Command(int type) {
		this((byte) type);
	}

	public Command(byte type) {
		this(type, new byte[0]);
	}

	public Command(int type, byte[] parametersOrData) {
		this((byte) type, parametersOrData);
	}

	public Command(byte type, byte[] parametersOrData) {
		length = getLength(parametersOrData);
		frame = getFrame(type, parametersOrData);
	}

	private short getLength(byte[] parametersOrData) {
		return (short) (parametersOrData.length + 9);
	}

	private byte getParity(byte[] frame) {
		byte parity = frame[0];
		for (int i = 1; i < frame.length - 1; i++) {
			parity ^= frame[i];
		}
		return parity;
	}

	private byte[] getFrame(byte type, byte[] parametersOrData) {
		byte[] frame = new byte[length];
		ByteBuffer byteBuffer = ByteBuffer.wrap(frame);
		byteBuffer.put(FRAME_HEADER);
		byteBuffer.putShort(length);
		byteBuffer.put(type);
		byteBuffer.put(parametersOrData);
		byteBuffer.put(FRAME_END);
		byteBuffer.put(getParity(frame));
		return frame;
	}

	public short getLength() {
		return length;
	}

	public byte[] getFrame() {
		return frame;
	}
} public class DisplayTextCommand extends Command {
	public DisplayTextCommand(int x, int y, String text) {
		this((short) x, (short) y, text);
	}

	public DisplayTextCommand(short x, short y, String text) {
		super(0x30, getParametersOrData(x, y, text));
	}

	private static byte[] getParametersOrData(short x, short y, String text) {
		byte[] textBytes = text.getBytes(StandardCharsets.US_ASCII);
		byte[] parametersOrData = new byte[textBytes.length + 5];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.putShort(x);
		byteBuffer.putShort(y);
		byteBuffer.put(textBytes);
		byteBuffer.put((byte) 0x00);
		return parametersOrData;
	}
} public class DrawLineCommand extends Command {
	public DrawLineCommand(int x0, int y0, int x1, int y1) {
		this((short) x0, (short) y0, (short) x1, (short) y1);
	}

	public DrawLineCommand(short x0, short y0, short x1, short y1) {
		super(0x22, getParametersOrData(x0, y0, x1, y1));
	}

	private static byte[] getParametersOrData(short x0, short y0, short x1, short y1) {
		byte[] parametersOrData = new byte[8];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.putShort(x0);
		byteBuffer.putShort(y0);
		byteBuffer.putShort(x1);
		byteBuffer.putShort(y1);
		return parametersOrData;
	}
} public class DrawPointCommand extends Command {
	public DrawPointCommand(int x, int y) {
		this((short) x, (short) y);
	}

	public DrawPointCommand(short x, short y) {
		super(0x20, getParametersOrData(x, y));
	}

	private static byte[] getParametersOrData(short x, short y) {
		byte[] parametersOrData = new byte[4];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.putShort(x);
		byteBuffer.putShort(y);
		return parametersOrData;
	}
} public class DrawRectangleCommand extends Command {
	public DrawRectangleCommand(int x0, int y0, int x1, int y1) {
		this((short) x0, (short) y0, (short) x1, (short) y1);
	}

	public DrawRectangleCommand(short x0, short y0, short x1, short y1) {
		super(0x25, getParametersOrData(x0, y0, x1, y1));
	}

	private static byte[] getParametersOrData(short x0, short y0, short x1, short y1) {
		byte[] parametersOrData = new byte[8];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.putShort(x0);
		byteBuffer.putShort(y0);
		byteBuffer.putShort(x1);
		byteBuffer.putShort(y1);
		return parametersOrData;
	}
} public class FillRectangleCommand extends Command {
	public FillRectangleCommand(int x0, int y0, int x1, int y1) {
		this((short) x0, (short) y0, (short) x1, (short) y1);
	}

	public FillRectangleCommand(short x0, short y0, short x1, short y1) {
		super(0x24, getParametersOrData(x0, y0, x1, y1));
	}

	private static byte[] getParametersOrData(short x0, short y0, short x1, short y1) {
		byte[] parametersOrData = new byte[8];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.putShort(x0);
		byteBuffer.putShort(y0);
		byteBuffer.putShort(x1);
		byteBuffer.putShort(y1);
		return parametersOrData;
	}
} public class GetDisplayDirectionCommand extends Command {
	public GetDisplayDirectionCommand() {
		super(0x0C);
	}
} public class GetDrawingColorsCommand extends Command {
	public GetDrawingColorsCommand() {
		super(0x11);
	}
} public class GetFontSizeCommand extends Command {
	public GetFontSizeCommand() {
		super(0x1C);
	}
} public class RefreshAndUpdateCommand extends Command {
	public RefreshAndUpdateCommand() {
		super(0x0A);
	}
} public class SetDisplayDirectionCommand extends Command {
	public SetDisplayDirectionCommand(DisplayDirection displayDirection) {
		super(0x0D, getParametersOrData(displayDirection));
	}

	private static byte[] getParametersOrData(DisplayDirection displayDirection) {
		byte[] parametersOrData = new byte[1];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.put((byte) displayDirection.ordinal());
		return parametersOrData;
	}
} public class SetDrawingColorsCommand extends Command {
	public SetDrawingColorsCommand(DrawingColors drawingColors) {
		super(0x10, getParametersOrData(drawingColors));
	}

	private static byte[] getParametersOrData(DrawingColors drawingColors) {
		byte[] parametersOrData = new byte[2];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.put((byte) drawingColors.getForeground().ordinal());
		byteBuffer.put((byte) drawingColors.getBackground().ordinal());
		return parametersOrData;
	}
} public class SetFontSizeCommand extends Command {
	public SetFontSizeCommand(FontSize fontSize) {
		super(0x1E, getParametersOrData(fontSize));
	}

	private static byte[] getParametersOrData(FontSize fontSize) {
		byte[] parametersOrData = new byte[1];
		ByteBuffer byteBuffer = ByteBuffer.wrap(parametersOrData);
		byteBuffer.put((byte) (fontSize.ordinal() + 1));
		return parametersOrData;
	}
}
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import fr.romainmoreau.epaper.client.api.DisplayDirection;
import fr.romainmoreau.epaper.client.api.DrawingColors;
import fr.romainmoreau.epaper.client.api.EPaperException;
import fr.romainmoreau.epaper.client.api.EPaperResponseException;
import fr.romainmoreau.epaper.client.api.FontSize;
import fr.romainmoreau.epaper.client.common.AbstractEPaperClient;
import fr.romainmoreau.epaper.client.common.Colors;
import fr.romainmoreau.epaper.client.common.Coordinates;
import fr.romainmoreau.epaper.client.common.Lines;
import fr.romainmoreau.epaper.client.common.uart.command.ClearCommand;
import fr.romainmoreau.epaper.client.common.uart.command.Command;
import fr.romainmoreau.epaper.client.common.uart.command.DisplayTextCommand;
import fr.romainmoreau.epaper.client.common.uart.command.DrawLineCommand;
import fr.romainmoreau.epaper.client.common.uart.command.DrawPointCommand;
import fr.romainmoreau.epaper.client.common.uart.command.DrawRectangleCommand;
import fr.romainmoreau.epaper.client.common.uart.command.FillRectangleCommand;
import fr.romainmoreau.epaper.client.common.uart.command.GetDisplayDirectionCommand;
import fr.romainmoreau.epaper.client.common.uart.command.GetDrawingColorsCommand;
import fr.romainmoreau.epaper.client.common.uart.command.GetFontSizeCommand;
import fr.romainmoreau.epaper.client.common.uart.command.RefreshAndUpdateCommand;
import fr.romainmoreau.epaper.client.common.uart.command.SetDisplayDirectionCommand;
import fr.romainmoreau.epaper.client.common.uart.command.SetDrawingColorsCommand;
import fr.romainmoreau.epaper.client.common.uart.command.SetFontSizeCommand;
package fr.romainmoreau.epaper.client.common.uart;



public abstract class AbstractUARTEPaperClient extends AbstractEPaperClient {
	private static final byte[] RESPONSE_OK = "OK".getBytes(StandardCharsets.US_ASCII);

	private final long timeout;

	private final long receiveTimeout;

	public AbstractUARTEPaperClient(long timeout, long receiveTimeout) {
		this.timeout = timeout;
		this.receiveTimeout = receiveTimeout;
	}

	@Override
	public synchronized void refreshAndUpdate() throws IOException, EPaperException {
		sendCommand(new RefreshAndUpdateCommand());
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void clear() throws IOException, EPaperException {
		sendCommand(new ClearCommand());
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void drawPoint(int x, int y) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x, y);
		sendCommand(new DrawPointCommand(x, y));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void drawLine(int x0, int y0, int x1, int y1) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x0, y0);
		Coordinates.validateCoordinates(x1, y1);
		sendCommand(new DrawLineCommand(x0, y0, x1, y1));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void drawRectangle(int x0, int y0, int x1, int y1) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x0, y0);
		Coordinates.validateCoordinates(x1, y1);
		sendCommand(new DrawRectangleCommand(x0, y0, x1, y1));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void fillRectangle(int x0, int y0, int x1, int y1) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x0, y0);
		Coordinates.validateCoordinates(x1, y1);
		sendCommand(new FillRectangleCommand(x0, y0, x1, y1));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized void displayText(int x, int y, String text) throws IOException, EPaperException {
		Coordinates.validateCoordinates(x, y);
		Lines.validateText(text);
		sendCommand(new DisplayTextCommand(x, y, text));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized DrawingColors getDrawingColors() throws IOException, EPaperException {
		sendCommand(new GetDrawingColorsCommand());
		waitForResponse();
		checkResponsePresent();
		try {
			return ColorsUART.getDrawingColors(getResponse());
		} catch (Exception e) {
			throw new EPaperResponseException(getResponse(), e);
		}

	}

	@Override
	public synchronized void setDrawingColors(DrawingColors drawingColors) throws IOException, EPaperException {
		Colors.validateDrawingColors(drawingColors);
		sendCommand(new SetDrawingColorsCommand(drawingColors));
		waitForResponse();
		checkResponseOK();
	}

	@Override
	public synchronized FontSize getFontSize() throws IOException, EPaperException {
		sendCommand(new GetFontSizeCommand());