diff --git a/.github/workflows/custom_build.yml b/.github/workflows/custom_build.yml index ec19430..66fbd67 100644 --- a/.github/workflows/custom_build.yml +++ b/.github/workflows/custom_build.yml @@ -99,11 +99,9 @@ jobs: echo "Building for target: ${{ inputs.target }}" echo "Flags: ${{ inputs.flags }}" - # Inject flags into platformio.ini or environment if needed - # For now, we rely on PIO's ability to take env vars or just run the target - # Real implementation might need more complex flag handling - - # Example: export PLATFORMIO_BUILD_FLAGS="${{ inputs.flags }}" + # Inject flags into platformio.ini or environment + export PLATFORMIO_BUILD_FLAGS="${{ inputs.flags }}" + echo "PLATFORMIO_BUILD_FLAGS set to: $PLATFORMIO_BUILD_FLAGS" pio run -e ${{ inputs.target }} diff --git a/convex/builds.ts b/convex/builds.ts index f975a55..1d95e05 100644 --- a/convex/builds.ts +++ b/convex/builds.ts @@ -1,7 +1,8 @@ import { getAuthUserId } from "@convex-dev/auth/server"; import { v } from "convex/values"; -import { api, } from "./_generated/api"; +import { api } from "./_generated/api"; import { internalMutation, mutation, query } from "./_generated/server"; +import modulesData from "./modules.json"; /** * Normalizes a config object to a stable JSON string for hashing. @@ -95,16 +96,17 @@ export const triggerBuild = mutation({ } // Convert config object to flags string - const flags = Object.entries(profile.config) - .map(([key, value]) => { - if (value === true) return `-D${key}`; - if (typeof value === "number") return `-D${key}=${value}`; - if (typeof value === "string" && value.trim() !== "") - return `-D${key}=${value}`; - return null; - }) - .filter(Boolean) - .join(" "); + const flags: string[] = []; + + // Handle Modules (Inverted Logic: Default Excluded) + for (const module of modulesData.modules) { + // If config[id] is NOT false (explicitly included), we exclude it. + if (profile.config[module.id] !== false) { + flags.push(`-D${module.id}=1`); + } + } + + const flagsString = flags.join(" "); // Create build records for each target for (const target of profile.targets) { @@ -126,7 +128,7 @@ export const triggerBuild = mutation({ if (cached) { // Use cached artifact, skip GitHub workflow const artifactUrl = getR2ArtifactUrl(buildHash); - const buildId = await ctx.db.insert("builds", { + const _buildId = await ctx.db.insert("builds", { profileId: profile._id, target: target, githubRunId: 0, @@ -153,7 +155,7 @@ export const triggerBuild = mutation({ await ctx.scheduler.runAfter(0, api.actions.dispatchGithubBuild, { buildId: buildId, target: target, - flags: flags, + flags: flagsString, version: profile.version, buildHash: buildHash, }); @@ -237,17 +239,18 @@ export const retryBuild = mutation({ completedAt: undefined, }); - // Retry the build - const flags = Object.entries(profile.config) - .map(([key, value]) => { - if (value === true) return `-D${key}`; - if (typeof value === "number") return `-D${key}=${value}`; - if (typeof value === "string" && value.trim() !== "") - return `-D${key}=${value}`; - return null; - }) - .filter(Boolean) - .join(" "); + // Convert config object to flags string + const flags: string[] = []; + + // Handle Modules (Inverted Logic: Default Excluded) + for (const module of modulesData.modules) { + // If config[id] is NOT false (explicitly included), we exclude it. + if (profile.config[module.id] !== false) { + flags.push(`-D${module.id}=1`); + } + } + + const flagsString = flags.join(" "); // Compute build hash for retry const buildHash = await computeBuildHash( @@ -259,7 +262,7 @@ export const retryBuild = mutation({ await ctx.scheduler.runAfter(0, api.actions.dispatchGithubBuild, { buildId: args.buildId, target: build.target, - flags: flags, + flags: flagsString, version: profile.version, buildHash: buildHash, }); diff --git a/convex/modules.json b/convex/modules.json new file mode 100644 index 0000000..48c6a9e --- /dev/null +++ b/convex/modules.json @@ -0,0 +1,159 @@ +{ + "modules": [ + { + "id": "MESHTASTIC_EXCLUDE_ADMIN", + "name": "Admin", + "description": "Remote device configuration and management. Allows changing settings, reading device info, and rebooting nodes over the mesh network." + }, + { + "id": "MESHTASTIC_EXCLUDE_ATAK", + "name": "ATAK Plugin", + "description": "Integration with ATAK (Android Team Awareness Kit) for tactical situational awareness. Enables military/emergency response coordination." + }, + { + "id": "MESHTASTIC_EXCLUDE_AUDIO", + "name": "Audio", + "description": "Audio codec support for voice communication over the mesh." + }, + { + "id": "MESHTASTIC_EXCLUDE_BLUETOOTH", + "name": "Bluetooth", + "description": "Bluetooth connectivity for pairing with phones and apps. Required for mobile app communication on most devices." + }, + { + "id": "MESHTASTIC_EXCLUDE_CANNEDMESSAGES", + "name": "Canned Messages", + "description": "Pre-defined quick messages that can be sent with button presses. Useful for devices with limited input (no keyboard). Includes on-screen keyboard for some devices." + }, + { + "id": "MESHTASTIC_EXCLUDE_DETECTIONSENSOR", + "name": "Detection Sensor", + "description": "Motion/presence detection sensor integration. Broadcasts detection events when sensors trigger (PIR, door switches, etc.)." + }, + { + "id": "MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR", + "name": "Environmental Sensor", + "description": "Environmental monitoring sensors including temperature, humidity, pressure, air quality, and light sensors. Broadcasts telemetry data to the mesh." + }, + { + "id": "MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION", + "name": "External Notification", + "description": "Drive external LEDs, buzzers, and speakers for notifications. Plays RTTTL ringtones and can control GPIO outputs when messages arrive." + }, + { + "id": "MESHTASTIC_EXCLUDE_GPS", + "name": "GPS", + "description": "GPS receiver support for position tracking and sharing. Disabling prevents position broadcasts but the device still relays position packets for other nodes." + }, + { + "id": "MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY", + "name": "Health Telemetry", + "description": "Heart rate and health monitoring sensors (like MAX30102 pulse oximeter). Broadcasts health metrics to the mesh." + }, + { + "id": "MESHTASTIC_EXCLUDE_I2C", + "name": "I2C", + "description": "I2C bus support for external sensors and peripherals. Required for most sensor modules and OLED displays." + }, + { + "id": "MESHTASTIC_EXCLUDE_INPUTBROKER", + "name": "Input Broker", + "description": "Input device handling (buttons, encoders, touchscreens). Routes button presses to appropriate modules like Canned Messages." + }, + { + "id": "MESHTASTIC_EXCLUDE_MQTT", + "name": "MQTT", + "description": "MQTT client for cloud integration. Publishes mesh messages to MQTT brokers and subscribes to receive cloud messages. Enables IoT integration and remote monitoring." + }, + { + "id": "MESHTASTIC_EXCLUDE_NEIGHBORINFO", + "name": "Neighbor Info", + "description": "Broadcasts information about directly-reachable neighbor nodes including signal strength (SNR). Helps build mesh topology maps and track network health." + }, + { + "id": "MESHTASTIC_EXCLUDE_PAXCOUNTER", + "name": "Pax Counter", + "description": "Counts nearby WiFi and Bluetooth devices for crowd density estimation. Useful for people-counting in public spaces." + }, + { + "id": "MESHTASTIC_EXCLUDE_PKI", + "name": "PKI", + "description": "Public Key Infrastructure for enhanced security and key verification between nodes." + }, + { + "id": "MESHTASTIC_EXCLUDE_POWERMON", + "name": "Power Monitor", + "description": "Battery and power monitoring hardware support (INA260, INA219, etc.). Tracks voltage, current, and power consumption." + }, + { + "id": "MESHTASTIC_EXCLUDE_POWER_FSM", + "name": "Power FSM", + "description": "Power management finite state machine. Handles sleep modes, power state transitions, and battery optimization." + }, + { + "id": "MESHTASTIC_EXCLUDE_POWER_TELEMETRY", + "name": "Power Telemetry", + "description": "Broadcasts battery voltage, current, and power consumption data to the mesh. Different from Power Monitor which is the hardware interface." + }, + { + "id": "MESHTASTIC_EXCLUDE_POWERSTRESS", + "name": "Power Stress", + "description": "Power consumption testing tool. Stresses the device to measure battery life under various transmission patterns. For development/testing only." + }, + { + "id": "MESHTASTIC_EXCLUDE_RANGETEST", + "name": "Range Test", + "description": "Mesh range and signal quality testing. Sends periodic packets and logs signal strength (RSSI), SNR, and packet loss statistics to a file." + }, + { + "id": "MESHTASTIC_EXCLUDE_REMOTEHARDWARE", + "name": "Remote Hardware", + "description": "Remote GPIO control over the mesh. Read/write digital pins, read ADC values, and control hardware on remote nodes." + }, + { + "id": "MESHTASTIC_EXCLUDE_SCREEN", + "name": "Screen", + "description": "OLED/E-Ink display support. Shows messages, node info, and status on screen. Disabling saves power but removes visual feedback." + }, + { + "id": "MESHTASTIC_EXCLUDE_SERIAL", + "name": "Serial", + "description": "Serial port communication for sensors and external devices. Can relay serial data over the mesh and supports NMEA GPS bridging." + }, + { + "id": "MESHTASTIC_EXCLUDE_STOREFORWARD", + "name": "Store & Forward", + "description": "Message store-and-forward server for offline nodes. Router devices can cache messages and replay them when distant nodes reconnect. Requires PSRAM." + }, + { + "id": "MESHTASTIC_EXCLUDE_TEXTMESSAGE", + "name": "Text Messaging", + "description": "Send and receive text messages between nodes. Displays messages on OLED screens and forwards to connected apps. **Important:** Disabling prevents sending/receiving but the node still relays messages for others." + }, + { + "id": "MESHTASTIC_EXCLUDE_TRACEROUTE", + "name": "Traceroute", + "description": "Network path tracing tool. Shows the route packets take through the mesh, including all intermediate hops and hop limits." + }, + { + "id": "MESHTASTIC_EXCLUDE_TZ", + "name": "Timezone", + "description": "Timezone database support for local time display. Allows devices to show correct local time based on GPS position." + }, + { + "id": "MESHTASTIC_EXCLUDE_WAYPOINT", + "name": "Waypoint", + "description": "Share and display waypoints (points of interest) on the mesh. Shows waypoints on screen and in apps for navigation and location marking." + }, + { + "id": "MESHTASTIC_EXCLUDE_WEBSERVER", + "name": "Web Server", + "description": "Built-in web interface for device configuration. Automatically excluded if WiFi is disabled." + }, + { + "id": "MESHTASTIC_EXCLUDE_WIFI", + "name": "WiFi", + "description": "WiFi connectivity for network access, web server, and MQTT. Disabling saves power but removes WiFi features including the web interface." + } + ] +} diff --git a/package.json b/package.json index 54dc05c..d79f2d6 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "generate:versions": "node scripts/generate-versions.js", - "dev": "bun run generate:versions && bun run scan:options && vite", - "build": "bun run generate:versions && bun run scan:options && tsc && vite build", + "dev": "bun run generate:versions && vite", + "build": "bun run generate:versions && tsc && vite build", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview", "deploy": "npx convex deploy --cmd 'bun run build' && wrangler deploy" diff --git a/src/components/ModuleCard.tsx b/src/components/ModuleCard.tsx new file mode 100644 index 0000000..51c8309 --- /dev/null +++ b/src/components/ModuleCard.tsx @@ -0,0 +1,62 @@ +interface ModuleCardProps { + name: string; + description: string; + selected: boolean; + onClick: () => void; +} + +export function ModuleCard({ + name, + description, + selected, + onClick, +}: ModuleCardProps) { + return ( + + ); +} diff --git a/src/components/ProfileEditor.tsx b/src/components/ProfileEditor.tsx index 77a30ec..5e0762b 100644 --- a/src/components/ProfileEditor.tsx +++ b/src/components/ProfileEditor.tsx @@ -5,8 +5,10 @@ import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Input } from "@/components/ui/input"; import { api } from "../../convex/_generated/api"; +import modulesData from "../../convex/modules.json"; import { TARGETS } from "../constants/targets"; import { VERSIONS } from "../constants/versions"; +import { ModuleCard } from "./ModuleCard"; interface ProfileEditorProps { initialData?: any; @@ -26,10 +28,7 @@ export default function ProfileEditor({ defaultValues: initialData || { name: "", targets: [], - config: { - MESHTASTIC_EXCLUDE_MQTT: false, - MESHTASTIC_EXCLUDE_AUDIO: false, - }, + config: {}, version: VERSIONS[0], }, }); @@ -99,18 +98,22 @@ export default function ProfileEditor({ >
- +
-