mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-22 17:57:32 +00:00
Compare commits
8 Commits
status-mes
...
Message_Bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34ad7809f5 | ||
|
|
f0e751a592 | ||
|
|
a122b49504 | ||
|
|
5b3df5adeb | ||
|
|
94d85b3f42 | ||
|
|
726a5ccfeb | ||
|
|
bfe206d9b7 | ||
|
|
b446c3453d |
@@ -6,7 +6,6 @@
|
||||
#include "MessageStore.h"
|
||||
#include "NodeDB.h"
|
||||
#include "UIRenderer.h"
|
||||
#include "configuration.h"
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
@@ -20,7 +19,6 @@
|
||||
|
||||
// External declarations
|
||||
extern bool hasUnreadMessage;
|
||||
extern meshtastic_DeviceState devicestate;
|
||||
extern graphics::Screen *screen;
|
||||
|
||||
using graphics::Emote;
|
||||
@@ -49,7 +47,7 @@ static inline size_t utf8CharLen(uint8_t c)
|
||||
}
|
||||
|
||||
// Remove variation selectors (FE0F) and skin tone modifiers from emoji so they match your labels
|
||||
std::string normalizeEmoji(const std::string &s)
|
||||
static std::string normalizeEmoji(const std::string &s)
|
||||
{
|
||||
std::string out;
|
||||
for (size_t i = 0; i < s.size();) {
|
||||
@@ -82,6 +80,7 @@ uint32_t pauseStart = 0;
|
||||
bool waitingToReset = false;
|
||||
bool scrollStarted = false;
|
||||
static bool didReset = false;
|
||||
static constexpr int MESSAGE_BLOCK_GAP = 6;
|
||||
|
||||
void scrollUp()
|
||||
{
|
||||
@@ -111,22 +110,6 @@ void scrollDown()
|
||||
|
||||
void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount)
|
||||
{
|
||||
std::string renderLine;
|
||||
for (size_t i = 0; i < line.size();) {
|
||||
uint8_t c = (uint8_t)line[i];
|
||||
size_t len = utf8CharLen(c);
|
||||
if (c == 0xEF && i + 2 < line.size() && (uint8_t)line[i + 1] == 0xB8 && (uint8_t)line[i + 2] == 0x8F) {
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
if (c == 0xF0 && i + 3 < line.size() && (uint8_t)line[i + 1] == 0x9F && (uint8_t)line[i + 2] == 0x8F &&
|
||||
((uint8_t)line[i + 3] >= 0xBB && (uint8_t)line[i + 3] <= 0xBF)) {
|
||||
i += 4;
|
||||
continue;
|
||||
}
|
||||
renderLine.append(line, i, len);
|
||||
i += len;
|
||||
}
|
||||
int cursorX = x;
|
||||
const int fontHeight = FONT_HEIGHT_SMALL;
|
||||
|
||||
@@ -203,8 +186,7 @@ void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string
|
||||
|
||||
// Render the emote (if found)
|
||||
if (matchedEmote && i == nextEmotePos) {
|
||||
// Vertically center emote relative to font baseline (not just midline)
|
||||
int iconY = fontY + (fontHeight - matchedEmote->height) / 2;
|
||||
int iconY = y + (lineHeight - matchedEmote->height) / 2;
|
||||
display->drawXbm(cursorX, iconY, matchedEmote->width, matchedEmote->height, matchedEmote->bitmap);
|
||||
cursorX += matchedEmote->width + 1;
|
||||
i += emojiLen;
|
||||
@@ -423,6 +405,102 @@ static inline int getRenderedLineWidth(OLEDDisplay *display, const std::string &
|
||||
return totalWidth;
|
||||
}
|
||||
|
||||
struct MessageBlock {
|
||||
size_t start;
|
||||
size_t end;
|
||||
bool mine;
|
||||
};
|
||||
|
||||
static int getDrawnLinePixelBottom(int lineTopY, const std::string &line, bool isHeaderLine)
|
||||
{
|
||||
if (isHeaderLine) {
|
||||
return lineTopY + (FONT_HEIGHT_SMALL - 1);
|
||||
}
|
||||
|
||||
int tallest = FONT_HEIGHT_SMALL;
|
||||
for (int e = 0; e < numEmotes; ++e) {
|
||||
if (line.find(emotes[e].label) != std::string::npos) {
|
||||
if (emotes[e].height > tallest)
|
||||
tallest = emotes[e].height;
|
||||
}
|
||||
}
|
||||
|
||||
const int lineHeight = std::max(FONT_HEIGHT_SMALL, tallest);
|
||||
const int iconTop = lineTopY + (lineHeight - tallest) / 2;
|
||||
|
||||
return iconTop + tallest - 1;
|
||||
}
|
||||
|
||||
static void drawRoundedRectOutline(OLEDDisplay *display, int x, int y, int w, int h, int r)
|
||||
{
|
||||
if (w <= 1 || h <= 1)
|
||||
return;
|
||||
|
||||
if (r < 0)
|
||||
r = 0;
|
||||
|
||||
int maxR = (std::min(w, h) / 2) - 1;
|
||||
if (r > maxR)
|
||||
r = maxR;
|
||||
|
||||
if (r == 0) {
|
||||
display->drawRect(x, y, w, h);
|
||||
return;
|
||||
}
|
||||
|
||||
const int x0 = x;
|
||||
const int y0 = y;
|
||||
const int x1 = x + w - 1;
|
||||
const int y1 = y + h - 1;
|
||||
|
||||
// sides
|
||||
if (x0 + r <= x1 - r) {
|
||||
display->drawLine(x0 + r, y0, x1 - r, y0); // top
|
||||
display->drawLine(x0 + r, y1, x1 - r, y1); // bottom
|
||||
}
|
||||
if (y0 + r <= y1 - r) {
|
||||
display->drawLine(x0, y0 + r, x0, y1 - r); // left
|
||||
display->drawLine(x1, y0 + r, x1, y1 - r); // right
|
||||
}
|
||||
|
||||
// corner arcs
|
||||
display->drawCircleQuads(x0 + r, y0 + r, r, 2); // top left
|
||||
display->drawCircleQuads(x1 - r, y0 + r, r, 1); // top right
|
||||
display->drawCircleQuads(x1 - r, y1 - r, r, 8); // bottom right
|
||||
display->drawCircleQuads(x0 + r, y1 - r, r, 4); // bottom left
|
||||
}
|
||||
|
||||
static std::vector<MessageBlock> buildMessageBlocks(const std::vector<bool> &isHeaderVec, const std::vector<bool> &isMineVec)
|
||||
{
|
||||
std::vector<MessageBlock> blocks;
|
||||
if (isHeaderVec.empty())
|
||||
return blocks;
|
||||
|
||||
size_t start = 0;
|
||||
bool mine = isMineVec[0];
|
||||
|
||||
for (size_t i = 1; i < isHeaderVec.size(); ++i) {
|
||||
if (isHeaderVec[i]) {
|
||||
MessageBlock b;
|
||||
b.start = start;
|
||||
b.end = i - 1;
|
||||
b.mine = mine;
|
||||
blocks.push_back(b);
|
||||
|
||||
start = i;
|
||||
mine = isMineVec[i];
|
||||
}
|
||||
}
|
||||
|
||||
MessageBlock last;
|
||||
last.start = start;
|
||||
last.end = isHeaderVec.size() - 1;
|
||||
last.mine = mine;
|
||||
blocks.push_back(last);
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
static void drawMessageScrollbar(OLEDDisplay *display, int visibleHeight, int totalHeight, int scrollOffset, int startY)
|
||||
{
|
||||
if (totalHeight <= visibleHeight)
|
||||
@@ -482,9 +560,14 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
constexpr int LEFT_MARGIN = 2;
|
||||
constexpr int RIGHT_MARGIN = 2;
|
||||
constexpr int SCROLLBAR_WIDTH = 3;
|
||||
constexpr int BUBBLE_PAD_X = 3;
|
||||
constexpr int BUBBLE_PAD_Y = 4;
|
||||
constexpr int BUBBLE_RADIUS = 4;
|
||||
constexpr int BUBBLE_MIN_W = 24;
|
||||
constexpr int BUBBLE_TEXT_INDENT = 2;
|
||||
|
||||
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN;
|
||||
|
||||
// Derived widths
|
||||
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (BUBBLE_PAD_X * 2);
|
||||
const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH;
|
||||
|
||||
// Title string depending on mode
|
||||
@@ -547,7 +630,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
char chanType[32] = "";
|
||||
if (currentMode == ThreadMode::ALL) {
|
||||
if (m.dest == NODENUM_BROADCAST) {
|
||||
snprintf(chanType, sizeof(chanType), "#%s", channels.getName(m.channelIndex));
|
||||
const char *name = channels.getName(m.channelIndex);
|
||||
if (currentResolution == ScreenResolution::Low || currentResolution == ScreenResolution::UltraLow) {
|
||||
if (strcmp(name, "ShortTurbo") == 0)
|
||||
name = "ShortT";
|
||||
else if (strcmp(name, "ShortSlow") == 0)
|
||||
name = "ShortS";
|
||||
else if (strcmp(name, "ShortFast") == 0)
|
||||
name = "ShortF";
|
||||
else if (strcmp(name, "MediumSlow") == 0)
|
||||
name = "MedS";
|
||||
else if (strcmp(name, "MediumFast") == 0)
|
||||
name = "MedF";
|
||||
else if (strcmp(name, "LongSlow") == 0)
|
||||
name = "LongS";
|
||||
else if (strcmp(name, "LongFast") == 0)
|
||||
name = "LongF";
|
||||
else if (strcmp(name, "LongTurbo") == 0)
|
||||
name = "LongT";
|
||||
else if (strcmp(name, "LongMod") == 0)
|
||||
name = "LongM";
|
||||
}
|
||||
snprintf(chanType, sizeof(chanType), "#%s", name);
|
||||
} else {
|
||||
snprintf(chanType, sizeof(chanType), "(DM)");
|
||||
}
|
||||
@@ -614,8 +718,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
}
|
||||
|
||||
// Shrink Sender name if needed
|
||||
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) -
|
||||
display->getStringWidth(" @...") - 10;
|
||||
int availWidth = (mine ? rightTextWidth : leftTextWidth) - display->getStringWidth(timeBuf) -
|
||||
display->getStringWidth(chanType) - display->getStringWidth(" @...");
|
||||
if (availWidth < 0)
|
||||
availWidth = 0;
|
||||
|
||||
@@ -667,6 +771,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
cachedLines = allLines;
|
||||
cachedHeights = calculateLineHeights(cachedLines, emotes, isHeader);
|
||||
|
||||
std::vector<MessageBlock> blocks = buildMessageBlocks(isHeader, isMine);
|
||||
|
||||
// Scrolling logic (unchanged)
|
||||
int totalHeight = 0;
|
||||
for (size_t i = 0; i < cachedHeights.size(); ++i)
|
||||
@@ -714,12 +820,123 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
|
||||
int finalScroll = (int)scrollY;
|
||||
int yOffset = -finalScroll + getTextPositions(display)[1];
|
||||
const int contentTop = getTextPositions(display)[1];
|
||||
const int contentBottom = scrollBottom; // already excludes nav line
|
||||
const int rightEdge = SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN;
|
||||
const int bubbleGapY = std::max(1, MESSAGE_BLOCK_GAP / 2);
|
||||
|
||||
std::vector<int> lineTop;
|
||||
lineTop.resize(cachedLines.size());
|
||||
{
|
||||
int acc = 0;
|
||||
for (size_t i = 0; i < cachedLines.size(); ++i) {
|
||||
lineTop[i] = yOffset + acc;
|
||||
acc += cachedHeights[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Draw bubbles
|
||||
for (size_t bi = 0; bi < blocks.size(); ++bi) {
|
||||
const auto &b = blocks[bi];
|
||||
if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end)
|
||||
continue;
|
||||
|
||||
int visualTop = lineTop[b.start];
|
||||
|
||||
int topY;
|
||||
if (isHeader[b.start]) {
|
||||
// Header start
|
||||
constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2
|
||||
topY = visualTop - BUBBLE_PAD_TOP_HEADER;
|
||||
} else {
|
||||
// Body start
|
||||
bool thisLineHasEmote = false;
|
||||
for (int e = 0; e < numEmotes; ++e) {
|
||||
if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) {
|
||||
thisLineHasEmote = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (thisLineHasEmote) {
|
||||
constexpr int EMOTE_PADDING_ABOVE = 4;
|
||||
visualTop -= EMOTE_PADDING_ABOVE;
|
||||
}
|
||||
topY = visualTop - BUBBLE_PAD_Y;
|
||||
}
|
||||
int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
|
||||
int bottomY = visualBottom + BUBBLE_PAD_Y;
|
||||
|
||||
if (bi + 1 < blocks.size()) {
|
||||
int nextHeaderIndex = (int)blocks[bi + 1].start;
|
||||
int nextTop = lineTop[nextHeaderIndex];
|
||||
int maxBottom = nextTop - 1 - bubbleGapY;
|
||||
if (bottomY > maxBottom)
|
||||
bottomY = maxBottom;
|
||||
}
|
||||
|
||||
if (bottomY <= topY + 2)
|
||||
continue;
|
||||
|
||||
if (bottomY < contentTop || topY > contentBottom - 1)
|
||||
continue;
|
||||
|
||||
int maxLineW = 0;
|
||||
|
||||
for (size_t i = b.start; i <= b.end; ++i) {
|
||||
int w = 0;
|
||||
if (isHeader[i]) {
|
||||
w = display->getStringWidth(cachedLines[i].c_str());
|
||||
if (b.mine)
|
||||
w += 12; // room for ACK/NACK/relay mark
|
||||
} else {
|
||||
w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
|
||||
}
|
||||
if (w > maxLineW)
|
||||
maxLineW = w;
|
||||
}
|
||||
|
||||
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (BUBBLE_PAD_X * 2));
|
||||
int bubbleH = (bottomY - topY) + 1;
|
||||
int bubbleX = 0;
|
||||
if (b.mine) {
|
||||
bubbleX = rightEdge - bubbleW;
|
||||
} else {
|
||||
bubbleX = x;
|
||||
}
|
||||
if (bubbleX < x)
|
||||
bubbleX = x;
|
||||
if (bubbleX + bubbleW > rightEdge)
|
||||
bubbleW = std::max(1, rightEdge - bubbleX);
|
||||
|
||||
if (bubbleW > 1 && bubbleH > 1) {
|
||||
int r = BUBBLE_RADIUS;
|
||||
int maxR = (std::min(bubbleW, bubbleH) / 2) - 1;
|
||||
if (maxR < 0)
|
||||
maxR = 0;
|
||||
if (r > maxR)
|
||||
r = maxR;
|
||||
|
||||
drawRoundedRectOutline(display, bubbleX, topY, bubbleW, bubbleH, r);
|
||||
const int extra = 3;
|
||||
const int rr = r + extra;
|
||||
int x1 = bubbleX + bubbleW - 1;
|
||||
int y1 = topY + bubbleH - 1;
|
||||
|
||||
if (!b.mine) {
|
||||
// top-left corner square
|
||||
display->drawLine(bubbleX, topY, bubbleX + rr, topY);
|
||||
display->drawLine(bubbleX, topY, bubbleX, topY + rr);
|
||||
} else {
|
||||
// bottom-right corner square
|
||||
display->drawLine(x1 - rr, y1, x1, y1);
|
||||
display->drawLine(x1, y1 - rr, x1, y1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render visible lines
|
||||
int lineY = yOffset;
|
||||
for (size_t i = 0; i < cachedLines.size(); ++i) {
|
||||
int lineY = yOffset;
|
||||
for (size_t j = 0; j < i; ++j)
|
||||
lineY += cachedHeights[j];
|
||||
|
||||
if (lineY > -cachedHeights[i] && lineY < scrollBottom) {
|
||||
if (isHeader[i]) {
|
||||
@@ -728,14 +945,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
int headerX;
|
||||
if (isMine[i]) {
|
||||
// push header left to avoid overlap with scrollbar
|
||||
headerX = SCREEN_WIDTH - w - SCROLLBAR_WIDTH - RIGHT_MARGIN;
|
||||
headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - BUBBLE_TEXT_INDENT;
|
||||
if (headerX < LEFT_MARGIN)
|
||||
headerX = LEFT_MARGIN;
|
||||
} else {
|
||||
headerX = x;
|
||||
headerX = x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT;
|
||||
}
|
||||
display->drawString(headerX, lineY, cachedLines[i].c_str());
|
||||
|
||||
// Draw underline just under header text
|
||||
int underlineY = lineY + FONT_HEIGHT_SMALL;
|
||||
|
||||
int underlineW = w;
|
||||
int maxW = rightEdge - headerX;
|
||||
if (maxW < 0)
|
||||
maxW = 0;
|
||||
if (underlineW > maxW)
|
||||
underlineW = maxW;
|
||||
|
||||
for (int px = 0; px < underlineW; ++px) {
|
||||
display->setPixel(headerX + px, underlineY);
|
||||
}
|
||||
|
||||
// Draw ACK/NACK mark for our own messages
|
||||
if (isMine[i]) {
|
||||
int markX = headerX - 10;
|
||||
@@ -753,32 +984,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
// AckStatus::NONE → show nothing
|
||||
}
|
||||
|
||||
// Draw underline just under header text
|
||||
int underlineY = lineY + FONT_HEIGHT_SMALL;
|
||||
for (int px = 0; px < w; ++px) {
|
||||
display->setPixel(headerX + px, underlineY);
|
||||
}
|
||||
} else {
|
||||
// Render message line
|
||||
if (isMine[i]) {
|
||||
// Calculate actual rendered width including emotes
|
||||
int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
|
||||
int rightX = SCREEN_WIDTH - renderedWidth - SCROLLBAR_WIDTH - RIGHT_MARGIN;
|
||||
int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - BUBBLE_TEXT_INDENT;
|
||||
if (rightX < LEFT_MARGIN)
|
||||
rightX = LEFT_MARGIN;
|
||||
|
||||
drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes);
|
||||
} else {
|
||||
drawStringWithEmotes(display, x, lineY, cachedLines[i], emotes, numEmotes);
|
||||
drawStringWithEmotes(display, x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT, lineY, cachedLines[i], emotes,
|
||||
numEmotes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lineY += cachedHeights[i];
|
||||
}
|
||||
int totalContentHeight = totalHeight;
|
||||
int visibleHeight = usableHeight;
|
||||
|
||||
// Draw scrollbar
|
||||
drawMessageScrollbar(display, visibleHeight, totalContentHeight, finalScroll, getTextPositions(display)[1]);
|
||||
drawMessageScrollbar(display, usableHeight, totalHeight, finalScroll, getTextPositions(display)[1]);
|
||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||
graphics::drawCommonFooter(display, x, y);
|
||||
}
|
||||
@@ -841,7 +1068,6 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
|
||||
constexpr int HEADER_UNDERLINE_GAP = 0; // space between underline and first body line
|
||||
constexpr int HEADER_UNDERLINE_PIX = 1; // underline thickness (1px row drawn)
|
||||
constexpr int BODY_LINE_LEADING = -4; // default vertical leading for normal body lines
|
||||
constexpr int MESSAGE_BLOCK_GAP = 4; // gap after a message block before a new header
|
||||
constexpr int EMOTE_PADDING_ABOVE = 4; // space above emote line (added to line above)
|
||||
constexpr int EMOTE_PADDING_BELOW = 3; // space below emote line (added to emote line)
|
||||
|
||||
@@ -851,6 +1077,7 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
|
||||
for (size_t idx = 0; idx < lines.size(); ++idx) {
|
||||
const auto &line = lines[idx];
|
||||
const int baseHeight = FONT_HEIGHT_SMALL;
|
||||
int lineHeight = baseHeight;
|
||||
|
||||
// Detect if THIS line or NEXT line contains an emote
|
||||
bool hasEmote = false;
|
||||
@@ -872,8 +1099,6 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
|
||||
}
|
||||
}
|
||||
|
||||
int lineHeight = baseHeight;
|
||||
|
||||
if (isHeaderVec[idx]) {
|
||||
// Header line spacing
|
||||
lineHeight = baseHeight + HEADER_UNDERLINE_PIX + HEADER_UNDERLINE_GAP;
|
||||
@@ -922,7 +1147,7 @@ void handleNewMessage(OLEDDisplay *display, const StoredMessage &sm, const mesht
|
||||
|
||||
// Banner logic
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet.from);
|
||||
char longName[48] = "???";
|
||||
char longName[48] = "?";
|
||||
if (node && node->user.long_name) {
|
||||
strncpy(longName, node->user.long_name, sizeof(longName) - 1);
|
||||
longName[sizeof(longName) - 1] = '\0';
|
||||
|
||||
@@ -1410,15 +1410,6 @@ void NodeDB::loadFromDisk()
|
||||
if (portduino_config.has_configDisplayMode) {
|
||||
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)portduino_config.configDisplayMode;
|
||||
}
|
||||
if (portduino_config.has_statusMessage) {
|
||||
moduleConfig.has_statusmessage = true;
|
||||
strncpy(moduleConfig.statusmessage.node_status, portduino_config.statusMessage.c_str(),
|
||||
sizeof(moduleConfig.statusmessage.node_status));
|
||||
moduleConfig.statusmessage.node_status[sizeof(moduleConfig.statusmessage.node_status) - 1] = '\0';
|
||||
}
|
||||
if (portduino_config.enable_UDP) {
|
||||
config.network.enabled_protocols = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -1519,7 +1510,6 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
|
||||
moduleConfig.has_ambient_lighting = true;
|
||||
moduleConfig.has_audio = true;
|
||||
moduleConfig.has_paxcounter = true;
|
||||
moduleConfig.has_statusmessage = true;
|
||||
|
||||
success &=
|
||||
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
|
||||
|
||||
@@ -905,11 +905,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
|
||||
bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
|
||||
{
|
||||
bool shouldReboot = true;
|
||||
// If we are in an open transaction or configuring MQTT or Serial (which have validation), defer disabling Bluetooth
|
||||
// Otherwise, disable Bluetooth to prevent the phone from interfering with the config
|
||||
if (!hasOpenEditTransaction && !IS_ONE_OF(c.which_payload_variant, meshtastic_ModuleConfig_mqtt_tag,
|
||||
meshtastic_ModuleConfig_serial_tag, meshtastic_ModuleConfig_statusmessage_tag)) {
|
||||
if (!hasOpenEditTransaction &&
|
||||
!IS_ONE_OF(c.which_payload_variant, meshtastic_ModuleConfig_mqtt_tag, meshtastic_ModuleConfig_serial_tag)) {
|
||||
disableBluetooth();
|
||||
}
|
||||
|
||||
@@ -1001,14 +1000,8 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
|
||||
moduleConfig.has_paxcounter = true;
|
||||
moduleConfig.paxcounter = c.payload_variant.paxcounter;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_statusmessage_tag:
|
||||
LOG_INFO("Set module config: StatusMessage");
|
||||
moduleConfig.has_statusmessage = true;
|
||||
moduleConfig.statusmessage = c.payload_variant.statusmessage;
|
||||
shouldReboot = false;
|
||||
break;
|
||||
}
|
||||
saveChanges(SEGMENT_MODULECONFIG, shouldReboot);
|
||||
saveChanges(SEGMENT_MODULECONFIG);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1187,11 +1180,6 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
|
||||
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
|
||||
res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter;
|
||||
break;
|
||||
case meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG:
|
||||
LOG_INFO("Get module config: StatusMessage");
|
||||
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_statusmessage_tag;
|
||||
res.get_module_config_response.payload_variant.statusmessage = moduleConfig.statusmessage;
|
||||
break;
|
||||
}
|
||||
|
||||
// NOTE: The phone app needs to know the ls_secsvalue so it can properly expect sleep behavior.
|
||||
|
||||
@@ -108,9 +108,6 @@
|
||||
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||
#include "modules/DropzoneModule.h"
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUS
|
||||
#include "modules/StatusMessageModule.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
|
||||
@@ -168,9 +165,6 @@ void setupModules()
|
||||
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||
dropzoneModule = new DropzoneModule();
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_STATUS
|
||||
statusMessageModule = new StatusMessageModule();
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE
|
||||
new GenericThreadModule();
|
||||
#endif
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
#if !MESHTASTIC_EXCLUDE_STATUS
|
||||
|
||||
#include "StatusMessageModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "ProtobufModule.h"
|
||||
|
||||
StatusMessageModule *statusMessageModule;
|
||||
|
||||
int32_t StatusMessageModule::runOnce()
|
||||
{
|
||||
if (moduleConfig.has_statusmessage && moduleConfig.statusmessage.node_status[0] != '\0') {
|
||||
// create and send message with the status message set
|
||||
meshtastic_StatusMessage ourStatus = meshtastic_StatusMessage_init_zero;
|
||||
strncpy(ourStatus.status, moduleConfig.statusmessage.node_status, sizeof(ourStatus.status));
|
||||
ourStatus.status[sizeof(ourStatus.status) - 1] = '\0'; // ensure null termination
|
||||
meshtastic_MeshPacket *p = allocDataPacket();
|
||||
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
|
||||
meshtastic_StatusMessage_fields, &ourStatus);
|
||||
p->to = NODENUM_BROADCAST;
|
||||
p->decoded.want_response = false;
|
||||
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||
p->channel = 0;
|
||||
service->sendToMesh(p);
|
||||
}
|
||||
|
||||
return 1000 * 12 * 60 * 60;
|
||||
}
|
||||
|
||||
ProcessMessage StatusMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
meshtastic_StatusMessage incomingMessage;
|
||||
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_StatusMessage_fields,
|
||||
&incomingMessage)) {
|
||||
LOG_INFO("Received a NodeStatus message %s", incomingMessage.status);
|
||||
}
|
||||
}
|
||||
return ProcessMessage::CONTINUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#if !MESHTASTIC_EXCLUDE_STATUS
|
||||
#include "SinglePortModule.h"
|
||||
#include "configuration.h"
|
||||
|
||||
class StatusMessageModule : public SinglePortModule, private concurrency::OSThread
|
||||
{
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
StatusMessageModule()
|
||||
: SinglePortModule("statusMessage", meshtastic_PortNum_NODE_STATUS_APP), concurrency::OSThread("StatusMessage")
|
||||
{
|
||||
if (moduleConfig.has_statusmessage && moduleConfig.statusmessage.node_status[0] != '\0') {
|
||||
this->setInterval(2 * 60 * 1000);
|
||||
} else {
|
||||
this->setInterval(1000 * 12 * 60 * 60);
|
||||
}
|
||||
// TODO: If we have a string, set the initial delay (15 minutes maybe)
|
||||
}
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
*/
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
extern StatusMessageModule *statusMessageModule;
|
||||
#endif
|
||||
@@ -65,7 +65,7 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
else
|
||||
checkConfigPort = false;
|
||||
printf("Using config file %d\n", TCPPort);
|
||||
printf("Using config file %d\n", TCPPort);
|
||||
break;
|
||||
case 'c':
|
||||
configPath = arg;
|
||||
@@ -847,7 +847,6 @@ bool loadConfig(const char *configPath)
|
||||
}
|
||||
|
||||
if (yamlConfig["Config"]) {
|
||||
portduino_config.has_config_overrides = true;
|
||||
if (yamlConfig["Config"]["DisplayMode"]) {
|
||||
portduino_config.has_configDisplayMode = true;
|
||||
if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "TWOCOLOR") {
|
||||
@@ -860,13 +859,6 @@ bool loadConfig(const char *configPath)
|
||||
portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT;
|
||||
}
|
||||
}
|
||||
if (yamlConfig["Config"]["StatusMessage"]) {
|
||||
portduino_config.has_statusMessage = true;
|
||||
portduino_config.statusMessage = (yamlConfig["Config"]["StatusMessage"]).as<std::string>("");
|
||||
}
|
||||
if ((yamlConfig["Config"]["EnableUDP"]).as<bool>(false)) {
|
||||
portduino_config.enable_UDP = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (yamlConfig["General"]) {
|
||||
@@ -882,8 +874,10 @@ bool loadConfig(const char *configPath)
|
||||
}
|
||||
if (checkConfigPort) {
|
||||
portduino_config.api_port = (yamlConfig["General"]["APIPort"]).as<int>(-1);
|
||||
if (portduino_config.api_port != -1 && portduino_config.api_port > 1023 && portduino_config.api_port < 65536) {
|
||||
TCPPort = (portduino_config.api_port);
|
||||
if (portduino_config.api_port != -1 &&
|
||||
portduino_config.api_port > 1023 &&
|
||||
portduino_config.api_port < 65536) {
|
||||
TCPPort = (portduino_config.api_port);
|
||||
}
|
||||
}
|
||||
portduino_config.mac_address = (yamlConfig["General"]["MACAddress"]).as<std::string>("");
|
||||
|
||||
@@ -168,12 +168,8 @@ extern struct portduino_config_struct {
|
||||
int hostMetrics_channel = 0;
|
||||
|
||||
// config
|
||||
bool has_config_overrides = false;
|
||||
int configDisplayMode = 0;
|
||||
bool has_configDisplayMode = false;
|
||||
std::string statusMessage = "";
|
||||
bool has_statusMessage = false;
|
||||
bool enable_UDP = false;
|
||||
|
||||
// General
|
||||
std::string mac_address = "";
|
||||
@@ -489,30 +485,21 @@ extern struct portduino_config_struct {
|
||||
}
|
||||
|
||||
// config
|
||||
if (has_config_overrides) {
|
||||
if (has_configDisplayMode) {
|
||||
out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
|
||||
if (has_configDisplayMode) {
|
||||
|
||||
switch (configDisplayMode) {
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_statusMessage) {
|
||||
out << YAML::Key << "StatusMessage" << YAML::Value << statusMessage;
|
||||
}
|
||||
if (enable_UDP) {
|
||||
out << YAML::Key << "EnableUDP" << YAML::Value << true;
|
||||
switch (configDisplayMode) {
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
|
||||
break;
|
||||
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
|
||||
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
|
||||
break;
|
||||
}
|
||||
|
||||
out << YAML::EndMap; // Config
|
||||
|
||||
Reference in New Issue
Block a user