initial commit

This commit is contained in:
2026-01-08 19:16:32 +00:00
commit d89aedd5af
121 changed files with 8509 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
//
// BleStatusView.swift
// iris
//
// Created by Codex.
//
import SwiftUI
struct BleStatusView: View {
@EnvironmentObject private var ble: BlePeripheralManager
@EnvironmentObject private var orchestrator: ContextOrchestrator
var body: some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .leading, spacing: 6) {
Text("GlassNow BLE")
.font(.title2.bold())
Text("Bluetooth: \(bluetoothStateText)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Toggle(isOn: $ble.advertisingEnabled) {
VStack(alignment: .leading, spacing: 2) {
Text("Advertising")
.font(.headline)
Text(ble.isAdvertising ? "On" : "Off")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.onChange(of: ble.advertisingEnabled) { _ in
ble.start()
}
VStack(alignment: .leading, spacing: 8) {
Text("Connection")
.font(.headline)
Text("Subscribed: \(ble.isSubscribed ? "Yes" : "No")")
.font(.subheadline)
Text("Subscribers: \(ble.subscribedCount)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
VStack(alignment: .leading, spacing: 8) {
Text("Telemetry")
.font(.headline)
Text("Last msgId: \(ble.lastMsgIdSent)")
.font(.subheadline)
Text("Last ping: \(ble.lastPingAt.map { timeOnly(from: $0) } ?? "Never")")
.font(.subheadline)
Text("Last data: \(ble.lastDataAt.map { timeOnly(from: $0) } ?? "Never")")
.font(.subheadline)
Text("Notify queue: \(ble.notifyQueueDepth)")
.font(.subheadline)
.foregroundStyle(.secondary)
if ble.droppedNotifyPackets > 0 {
Text("Dropped notify packets: \(ble.droppedNotifyPackets)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
Text("Last notify: \(ble.lastNotifyAt.map { timeOnly(from: $0) } ?? "Never")")
.font(.subheadline)
.foregroundStyle(.secondary)
if let cmd = ble.lastCommand, !cmd.isEmpty {
Text("Last control: \(cmd)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
VStack(alignment: .leading, spacing: 12) {
Button("Send Fixture Feed Now") {
orchestrator.sendFixtureFeedNow()
}
.buttonStyle(.borderedProminent)
Button("Copy UUIDs") {
ble.copyUUIDsToPasteboard()
}
.buttonStyle(.bordered)
}
VStack(alignment: .leading, spacing: 8) {
Text("UUIDs")
.font(.headline)
Text("Service: \(BlePeripheralManager.serviceUUID.uuidString)\nFEED_TX: \(BlePeripheralManager.feedTxUUID.uuidString)\nCONTROL_RX: \(BlePeripheralManager.controlRxUUID.uuidString)")
.font(.caption)
.textSelection(.enabled)
}
}
.padding(.vertical, 16)
.padding(.horizontal, 24)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.onAppear { ble.start() }
}
private var bluetoothStateText: String {
switch ble.bluetoothState {
case .unknown: return "Unknown"
case .resetting: return "Resetting"
case .unsupported: return "Unsupported"
case .unauthorized: return "Unauthorized"
case .poweredOff: return "Powered Off"
case .poweredOn: return "Powered On"
@unknown default: return "Other"
}
}
private func timeOnly(from date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .medium
return formatter.string(from: date)
}
}
struct BleStatusView_Previews: PreviewProvider {
static var previews: some View {
Text("Preview unavailable (requires EnvironmentObjects).")
}
}