2026-01-07 03:58:37 -05:00
|
|
|
#pragma once
|
|
|
|
|
#include <SDCardManager.h>
|
2026-01-14 14:28:19 -05:00
|
|
|
#include <WiFiClient.h>
|
2026-01-07 03:58:37 -05:00
|
|
|
#include <WiFiUdp.h>
|
|
|
|
|
#include <freertos/FreeRTOS.h>
|
|
|
|
|
#include <freertos/semphr.h>
|
|
|
|
|
#include <freertos/task.h>
|
|
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
|
|
#include "activities/Activity.h"
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* CalibreWirelessActivity implements Calibre's "wireless device" protocol.
|
|
|
|
|
* This allows Calibre desktop to send books directly to the device over WiFi.
|
|
|
|
|
*
|
|
|
|
|
* Protocol specification sourced from Calibre's smart device driver:
|
|
|
|
|
* https://github.com/kovidgoyal/calibre/blob/master/src/calibre/devices/smart_device_app/driver.py
|
|
|
|
|
*/
|
|
|
|
|
class CalibreWirelessActivity final : public Activity {
|
2026-01-15 10:36:44 -05:00
|
|
|
enum class WirelessState { DISCOVERING, CONNECTING, WAITING, RECEIVING, COMPLETE, DISCONNECTED, ERROR };
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
enum OpCode : uint8_t {
|
|
|
|
|
OK = 0,
|
|
|
|
|
SET_CALIBRE_DEVICE_INFO = 1,
|
|
|
|
|
SET_CALIBRE_DEVICE_NAME = 2,
|
|
|
|
|
GET_DEVICE_INFORMATION = 3,
|
|
|
|
|
TOTAL_SPACE = 4,
|
|
|
|
|
FREE_SPACE = 5,
|
|
|
|
|
GET_BOOK_COUNT = 6,
|
|
|
|
|
SEND_BOOKLISTS = 7,
|
|
|
|
|
SEND_BOOK = 8,
|
|
|
|
|
GET_INITIALIZATION_INFO = 9,
|
|
|
|
|
BOOK_DONE = 11,
|
2026-01-14 14:18:58 -05:00
|
|
|
NOOP = 12,
|
2026-01-07 03:58:37 -05:00
|
|
|
DELETE_BOOK = 13,
|
|
|
|
|
GET_BOOK_FILE_SEGMENT = 14,
|
|
|
|
|
GET_BOOK_METADATA = 15,
|
|
|
|
|
SEND_BOOK_METADATA = 16,
|
|
|
|
|
DISPLAY_MESSAGE = 17,
|
|
|
|
|
CALIBRE_BUSY = 18,
|
|
|
|
|
SET_LIBRARY_INFO = 19,
|
|
|
|
|
ERROR = 20,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TaskHandle_t displayTaskHandle = nullptr;
|
2026-01-14 14:28:19 -05:00
|
|
|
TaskHandle_t networkTaskHandle = nullptr;
|
2026-01-07 03:58:37 -05:00
|
|
|
SemaphoreHandle_t renderingMutex = nullptr;
|
2026-01-14 14:28:19 -05:00
|
|
|
SemaphoreHandle_t stateMutex = nullptr;
|
2026-01-07 03:58:37 -05:00
|
|
|
bool updateRequired = false;
|
2026-01-14 14:18:58 -05:00
|
|
|
volatile bool shouldExit = false;
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
WirelessState state = WirelessState::DISCOVERING;
|
2026-01-14 14:18:58 -05:00
|
|
|
const std::function<void()> onCompleteCallback;
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
WiFiUDP udp;
|
2026-01-14 14:28:19 -05:00
|
|
|
WiFiClient tcpClient;
|
2026-01-07 03:58:37 -05:00
|
|
|
std::string calibreHost;
|
|
|
|
|
uint16_t calibrePort = 0;
|
2026-01-14 14:18:58 -05:00
|
|
|
uint16_t calibreAltPort = 0;
|
2026-01-07 03:58:37 -05:00
|
|
|
std::string calibreHostname;
|
|
|
|
|
|
|
|
|
|
std::string currentFilename;
|
|
|
|
|
size_t currentFileSize = 0;
|
|
|
|
|
size_t bytesReceived = 0;
|
|
|
|
|
std::string statusMessage;
|
|
|
|
|
std::string errorMessage;
|
|
|
|
|
|
|
|
|
|
bool inBinaryMode = false;
|
|
|
|
|
size_t binaryBytesRemaining = 0;
|
|
|
|
|
FsFile currentFile;
|
2026-01-14 14:28:19 -05:00
|
|
|
std::string recvBuffer;
|
2026-01-07 03:58:37 -05:00
|
|
|
|
2026-01-14 13:13:57 -05:00
|
|
|
bool inSkipMode = false;
|
|
|
|
|
size_t skipBytesRemaining = 0;
|
2026-01-14 14:18:58 -05:00
|
|
|
int skipOpcode = -1;
|
2026-01-14 13:13:57 -05:00
|
|
|
std::string skipExtractedLpath;
|
|
|
|
|
size_t skipExtractedLength = 0;
|
|
|
|
|
|
2026-01-07 03:58:37 -05:00
|
|
|
static void displayTaskTrampoline(void* param);
|
2026-01-14 14:28:19 -05:00
|
|
|
static void networkTaskTrampoline(void* param);
|
2026-01-14 14:18:58 -05:00
|
|
|
void displayTaskLoop();
|
2026-01-14 14:28:19 -05:00
|
|
|
void networkTaskLoop();
|
2026-01-07 03:58:37 -05:00
|
|
|
void render() const;
|
|
|
|
|
|
2026-01-14 14:28:19 -05:00
|
|
|
void listenForDiscovery();
|
|
|
|
|
void handleTcpClient();
|
|
|
|
|
bool readJsonMessage(std::string& message);
|
2026-01-07 03:58:37 -05:00
|
|
|
void sendJsonResponse(OpCode opcode, const std::string& data);
|
|
|
|
|
void handleCommand(OpCode opcode, const std::string& data);
|
2026-01-14 14:28:19 -05:00
|
|
|
void receiveBinaryData();
|
2026-01-07 03:58:37 -05:00
|
|
|
|
|
|
|
|
void handleGetInitializationInfo(const std::string& data);
|
|
|
|
|
void handleGetDeviceInformation();
|
|
|
|
|
void handleFreeSpace();
|
|
|
|
|
void handleGetBookCount();
|
|
|
|
|
void handleSendBook(const std::string& data);
|
|
|
|
|
void handleSendBookMetadata(const std::string& data);
|
|
|
|
|
void handleDisplayMessage(const std::string& data);
|
|
|
|
|
void handleNoop(const std::string& data);
|
|
|
|
|
|
|
|
|
|
std::string getDeviceUuid() const;
|
|
|
|
|
void setState(WirelessState newState);
|
|
|
|
|
void setStatus(const std::string& message);
|
|
|
|
|
void setError(const std::string& message);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit CalibreWirelessActivity(GfxRenderer& renderer, MappedInputManager& mappedInput,
|
|
|
|
|
const std::function<void()>& onComplete)
|
2026-01-14 14:18:58 -05:00
|
|
|
: Activity("CalibreWireless", renderer, mappedInput), onCompleteCallback(onComplete) {}
|
2026-01-07 03:58:37 -05:00
|
|
|
void onEnter() override;
|
|
|
|
|
void onExit() override;
|
|
|
|
|
void loop() override;
|
|
|
|
|
bool preventAutoSleep() override { return true; }
|
|
|
|
|
bool skipLoopDelay() override { return true; }
|
|
|
|
|
};
|