mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
commit
6e5c045226
92 changed files with 3234 additions and 2471 deletions
|
|
@ -2137,6 +2137,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Add Contact" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "增加聯絡人"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Add Meshtastic Node %@ as a contact" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "將 Meshtastic 節點 %@ 新增為聯絡人"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Add to favorites" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -2284,7 +2304,14 @@
|
|||
}
|
||||
},
|
||||
"Administration Enabled" : {
|
||||
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "管理功能已啟用"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Advanced" : {
|
||||
"localizations" : {
|
||||
|
|
@ -4043,6 +4070,7 @@
|
|||
}
|
||||
},
|
||||
"Battery Level" : {
|
||||
"comment" : "VoiceOver label for battery gauge",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -4101,6 +4129,7 @@
|
|||
}
|
||||
},
|
||||
"Battery Level %" : {
|
||||
"comment" : "VoiceOver value for battery level",
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -4156,40 +4185,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"biking" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "biken"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tour in bicicletta"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "тура бициклом"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "自行车旅行"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "自行車"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Biking" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -5010,6 +5005,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Bytes Used" : {
|
||||
"comment" : "VoiceOver value for bytes used"
|
||||
},
|
||||
"Call Sign" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -7202,6 +7200,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Contact URL" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "聯絡人網址"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Contacts (%@)" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -9622,6 +9630,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Disconnect Node" : {
|
||||
|
||||
},
|
||||
"Disconnect the currently connected node" : {
|
||||
|
||||
},
|
||||
"Dismiss" : {
|
||||
"localizations" : {
|
||||
|
|
@ -9995,6 +10009,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Done" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "完成"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Double Tap as Button" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -10193,40 +10217,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"driving" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "fahren"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "guida"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "вожња"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "驾驶"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "開車"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Driving" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -13994,6 +13984,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Hide sidebar" : {
|
||||
|
||||
},
|
||||
"HIGH" : {
|
||||
"localizations" : {
|
||||
|
|
@ -14029,40 +14022,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"hiking" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "wandern"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "escursione"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "планинарње"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "徒步"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "登山"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Hiking" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -17372,9 +17331,6 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"message" : {
|
||||
|
||||
},
|
||||
"Message" : {
|
||||
"localizations" : {
|
||||
|
|
@ -17606,6 +17562,9 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Message Size" : {
|
||||
"comment" : "VoiceOver label for message size"
|
||||
},
|
||||
"Message Status Options" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -17707,6 +17666,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Messaging" : {
|
||||
|
||||
},
|
||||
"Metric" : {
|
||||
"localizations" : {
|
||||
|
|
@ -21260,34 +21222,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"overlanding" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "overland drive"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Вожња преко копна"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "越野"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "開車 (overland drive)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Overlanding" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -21617,6 +21551,12 @@
|
|||
"state" : "translated",
|
||||
"value" : "Конфигурација PAX бројача примљена: %@"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "PAX 計數器設定已收到: %@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -21701,7 +21641,14 @@
|
|||
}
|
||||
},
|
||||
"paxcounter.log" : {
|
||||
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "paxcounter.log"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Perform a factory reset on the node you are connected to" : {
|
||||
"localizations" : {
|
||||
|
|
@ -22193,6 +22140,58 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Position config received: %@" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Positionskonfiguration empfangen: %@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Configuration de la position reçue : %@"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "הגדרות מיקום התקבלו: %@"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Configurazione della posizione ricevuta: %@"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Odebrano konfigurację pozycji: %@"
|
||||
}
|
||||
},
|
||||
"se" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Positionskonfiguration mottagen: %@"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Конфигурација позиције примљена: %@"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "收到位置設定檔: %@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Position Exchange Failed" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -22467,52 +22466,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Positon config received: %@" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Positionskonfiguration empfangen: %@"
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Configuration de la position reçue : %@"
|
||||
}
|
||||
},
|
||||
"he" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "הגדרות מיקום התקבלו: %@"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Configurazione della posizione ricevuta: %@"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Odebrano konfigurację pozycji: %@"
|
||||
}
|
||||
},
|
||||
"se" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Positionskonfiguration mottagen: %@"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Конфигурација позиције примљена: %@"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Power" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -24498,7 +24451,14 @@
|
|||
}
|
||||
},
|
||||
"Replying to a message" : {
|
||||
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "正在回覆訊息"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Request Legacy Admin: %@" : {
|
||||
"localizations" : {
|
||||
|
|
@ -26022,6 +25982,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Scan this QR code to add %@ to another device." : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "掃描這個QR code 以便將 %@ 增加到另一個裝置。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Screen on for" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -26419,7 +26389,14 @@
|
|||
}
|
||||
},
|
||||
"Select a node from the drop down to manage connected or remote devices." : {
|
||||
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "從下拉選單中選擇一個節點,以管理已連接或遠端的裝置。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Select a Trace Route" : {
|
||||
"localizations" : {
|
||||
|
|
@ -28034,6 +28011,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Share Contact QR" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "分享聯絡人 QR 碼"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Share QR Code" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -28480,7 +28467,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Show Waypoints " : {
|
||||
"Show Waypoints" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
|
|
@ -28820,40 +28807,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"skiing" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "skitour"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "tour sciistico"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "ски тура"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "滑雪之旅"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "滑雪"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Skiing" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -29525,6 +29478,12 @@
|
|||
"state" : "translated",
|
||||
"value" : "Подсистем"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "子系統"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -29712,6 +29671,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Takes a Meshtastic contact URL and saves it to the nodes database" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "將 Meshtastic 聯絡人的網址儲存到節點資料庫中。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tapback" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
@ -30892,6 +30861,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"The URL for the node to add" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "要新增的節點網址"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"There has been no response to a request for device metadata over the admin channel for this node." : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -31154,28 +31133,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"This could take a while. The response will appear in the trace route log for the node it was sent to." : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "L'operazione potrebbe richiedere un po' di tempo. La risposta apparirà nel registro delle rotte di tracciamento per il nodo a cui è stata inviata."
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Ово може потрајати. Одговор ће се појавити у евиденцији трасе праћења за чвор којем је послат."
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "這可能需要一段時間。回應將會顯示在被發送節點的路由追蹤紀錄中。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"This device will send out range test messages on the selected interval." : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -31918,6 +31875,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Trace Route (in %@s)" : {
|
||||
"localizations" : {
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "追蹤路由(在 %@ 秒)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Trace Route Log" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
|
|
@ -32099,6 +32066,12 @@
|
|||
"state" : "translated",
|
||||
"value" : "追踪器"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "追蹤器"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -32798,6 +32771,28 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"unknown" : {
|
||||
"localizations" : {
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "sconosciuto"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "непознато"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "needs_review",
|
||||
"value" : "未知"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unknown" : {
|
||||
"localizations" : {
|
||||
"fr" : {
|
||||
|
|
@ -32907,6 +32902,12 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Unmessagable" : {
|
||||
|
||||
},
|
||||
"Unmonitored" : {
|
||||
|
||||
},
|
||||
"Unset" : {
|
||||
"localizations" : {
|
||||
|
|
@ -33503,6 +33504,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Used to identify unmonitored or infrastructure nodes so that messaging is not avaliable to nodes that will never respond." : {
|
||||
|
||||
},
|
||||
"User" : {
|
||||
"localizations" : {
|
||||
|
|
@ -33879,6 +33883,12 @@
|
|||
"state" : "new",
|
||||
"value" : "Version: %1$@ (%2$@)"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "版本: %1$@ (%2$@)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -34176,40 +34186,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"walk" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "gehen"
|
||||
}
|
||||
},
|
||||
"it" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "passeggiata"
|
||||
}
|
||||
},
|
||||
"sr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "шетња"
|
||||
}
|
||||
},
|
||||
"zh-Hans" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "步行"
|
||||
}
|
||||
},
|
||||
"zh-Hant-TW" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "走路"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Walking" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
108FFECB2DD3F43C00BFAA81 /* ShareContactQRDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 108FFECA2DD3F43C00BFAA81 /* ShareContactQRDialog.swift */; };
|
||||
108FFECD2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 108FFECC2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift */; };
|
||||
231B3F212D087A4C0069A07D /* MetricTableColumn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F202D087A4C0069A07D /* MetricTableColumn.swift */; };
|
||||
231B3F222D087A4C0069A07D /* MetricsColumnList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F1F2D087A4C0069A07D /* MetricsColumnList.swift */; };
|
||||
231B3F252D087C3C0069A07D /* EnvironmentDefaultColumns.swift in Sources */ = {isa = PBXBuildFile; fileRef = 231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */; };
|
||||
|
|
@ -28,6 +30,7 @@
|
|||
2373AE132D0A216C0086C749 /* MetricsChartSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */; };
|
||||
2373AE152D0A24930086C749 /* MetricsSeriesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE142D0A24930086C749 /* MetricsSeriesList.swift */; };
|
||||
2373AE172D0A26620086C749 /* EnvironmentDefaultSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */; };
|
||||
237B46962DC8F1C100B22D99 /* RateLimitedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 237B46952DC8F1C100B22D99 /* RateLimitedButton.swift */; };
|
||||
251926852C3BA97800249DF5 /* FavoriteNodeButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */; };
|
||||
251926872C3BAE2200249DF5 /* NodeAlertsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */; };
|
||||
2519268A2C3BB1B200249DF5 /* ExchangePositionsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */; };
|
||||
|
|
@ -55,12 +58,14 @@
|
|||
8D3F8A412D44C2A6009EAAA4 /* PowerMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */; };
|
||||
B399E8A42B6F486400E4488E /* RetryButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B399E8A32B6F486400E4488E /* RetryButton.swift */; };
|
||||
B3E905B12B71F7F300654D07 /* TextMessageField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3E905B02B71F7F300654D07 /* TextMessageField.swift */; };
|
||||
BC10380F2DD4334400B00BFA /* AddContactIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */; };
|
||||
BC47C2EF2CE0017D008245CA /* MessageNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */; };
|
||||
BC6B45FF2CB2F98900723CEB /* SaveChannelSettingsIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */; };
|
||||
BCB613812C67290800485544 /* SendWaypointIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613802C67290800485544 /* SendWaypointIntent.swift */; };
|
||||
BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613822C672A2600485544 /* MessageChannelIntent.swift */; };
|
||||
BCB613852C68703800485544 /* NodePositionIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613842C68703800485544 /* NodePositionIntent.swift */; };
|
||||
BCB613872C69A0FB00485544 /* AppIntentErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB613862C69A0FB00485544 /* AppIntentErrors.swift */; };
|
||||
BCD93CBA2D9E11A2006C9214 /* DisconnectNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD93CB92D9E11A2006C9214 /* DisconnectNodeIntent.swift */; };
|
||||
BCDDFA9A2DBB180D0065189C /* ScrollToBottomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */; };
|
||||
BCE2D3C32C7ADF42008E6199 /* ShutDownNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */; };
|
||||
BCE2D3C52C7AE369008E6199 /* RestartNodeIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */; };
|
||||
|
|
@ -273,6 +278,8 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
108FFECA2DD3F43C00BFAA81 /* ShareContactQRDialog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareContactQRDialog.swift; sourceTree = "<group>"; };
|
||||
108FFECC2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeInfoEntityToNodeInfo.swift; sourceTree = "<group>"; };
|
||||
231B3F1F2D087A4C0069A07D /* MetricsColumnList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsColumnList.swift; sourceTree = "<group>"; };
|
||||
231B3F202D087A4C0069A07D /* MetricTableColumn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricTableColumn.swift; sourceTree = "<group>"; };
|
||||
231B3F242D087C3C0069A07D /* EnvironmentDefaultColumns.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultColumns.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -294,6 +301,7 @@
|
|||
2373AE122D0A216C0086C749 /* MetricsChartSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsChartSeries.swift; sourceTree = "<group>"; };
|
||||
2373AE142D0A24930086C749 /* MetricsSeriesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsSeriesList.swift; sourceTree = "<group>"; };
|
||||
2373AE162D0A26620086C749 /* EnvironmentDefaultSeries.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentDefaultSeries.swift; sourceTree = "<group>"; };
|
||||
237B46952DC8F1C100B22D99 /* RateLimitedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateLimitedButton.swift; sourceTree = "<group>"; };
|
||||
251926842C3BA97800249DF5 /* FavoriteNodeButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteNodeButton.swift; sourceTree = "<group>"; };
|
||||
251926862C3BAE2200249DF5 /* NodeAlertsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodeAlertsButton.swift; sourceTree = "<group>"; };
|
||||
251926892C3BB1B200249DF5 /* ExchangePositionsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExchangePositionsButton.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -316,6 +324,7 @@
|
|||
8D3F8A402D44C2A6009EAAA4 /* PowerMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PowerMetricsLog.swift; sourceTree = "<group>"; };
|
||||
B399E8A32B6F486400E4488E /* RetryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetryButton.swift; sourceTree = "<group>"; };
|
||||
B3E905B02B71F7F300654D07 /* TextMessageField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextMessageField.swift; sourceTree = "<group>"; };
|
||||
BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactIntent.swift; sourceTree = "<group>"; };
|
||||
BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveChannelSettingsIntent.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -323,6 +332,7 @@
|
|||
BCB613822C672A2600485544 /* MessageChannelIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageChannelIntent.swift; sourceTree = "<group>"; };
|
||||
BCB613842C68703800485544 /* NodePositionIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NodePositionIntent.swift; sourceTree = "<group>"; };
|
||||
BCB613862C69A0FB00485544 /* AppIntentErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIntentErrors.swift; sourceTree = "<group>"; };
|
||||
BCD93CB92D9E11A2006C9214 /* DisconnectNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BCDDFA992DBB180D0065189C /* ScrollToBottomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollToBottomButton.swift; sourceTree = "<group>"; };
|
||||
BCE2D3C22C7ADF42008E6199 /* ShutDownNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShutDownNodeIntent.swift; sourceTree = "<group>"; };
|
||||
BCE2D3C42C7AE369008E6199 /* RestartNodeIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestartNodeIntent.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -409,6 +419,7 @@
|
|||
DD6193742862F6E600E59241 /* ExternalNotificationConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExternalNotificationConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193762862F90F00E59241 /* CannedMessagesConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CannedMessagesConfig.swift; sourceTree = "<group>"; };
|
||||
DD6193782863875F00E59241 /* SerialConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfig.swift; sourceTree = "<group>"; };
|
||||
DD63CB4E2DD4FBEA00AFCAE2 /* MeshtasticDataModelV 51.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 51.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD68BAE72C417A74004C01A0 /* MeshtasticDataModelV 40.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 40.xcdatamodel"; sourceTree = "<group>"; };
|
||||
DD6D5A322CA1178300ED3032 /* TraceRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceRoute.swift; sourceTree = "<group>"; };
|
||||
DD6D5A342CA13BA600ED3032 /* MeshtasticDataModelV 45.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "MeshtasticDataModelV 45.xcdatamodel"; sourceTree = "<group>"; };
|
||||
|
|
@ -670,6 +681,7 @@
|
|||
BCB6137F2C6728E700485544 /* AppIntents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BC10380E2DD4333C00B00BFA /* AddContactIntent.swift */,
|
||||
BC5EBA3B2D002A2000C442FF /* MessageNodeIntent.swift */,
|
||||
BCB613802C67290800485544 /* SendWaypointIntent.swift */,
|
||||
BCB613822C672A2600485544 /* MessageChannelIntent.swift */,
|
||||
|
|
@ -681,6 +693,7 @@
|
|||
BCE2D3C62C7B0D0A008E6199 /* ShortcutsProvider.swift */,
|
||||
BC6B45FE2CB2F98900723CEB /* SaveChannelSettingsIntent.swift */,
|
||||
BC47C2EE2CE0017D008245CA /* MessageNodeIntent.swift */,
|
||||
BCD93CB92D9E11A2006C9214 /* DisconnectNodeIntent.swift */,
|
||||
);
|
||||
path = AppIntents;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -699,6 +712,7 @@
|
|||
DD007BB12AA59B9A00F5FA12 /* CoreData */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
108FFECC2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift */,
|
||||
2344A2AA2D66973D00170A77 /* ManagedAttributePropertyWrapper.swift */,
|
||||
DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */,
|
||||
6DEDA55B2A9592F900321D2E /* MessageEntityExtension.swift */,
|
||||
|
|
@ -1041,6 +1055,7 @@
|
|||
DD5E523D298F5A7D00D21B61 /* Weather */,
|
||||
DD6F65712C6AB8EC0053C113 /* SecureInput.swift */,
|
||||
8D3F8A3E2D44BB02009EAAA4 /* PowerMetrics.swift */,
|
||||
237B46952DC8F1C100B22D99 /* RateLimitedButton.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1098,6 +1113,7 @@
|
|||
DDDB26402AABEF7B003AFCB7 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
108FFECA2DD3F43C00BFAA81 /* ShareContactQRDialog.swift */,
|
||||
231B3F232D087C020069A07D /* Metrics Columns */,
|
||||
DDAD49EB2AFAE82500B4425D /* Map */,
|
||||
DDDB26432AAC0206003AFCB7 /* NodeDetail.swift */,
|
||||
|
|
@ -1393,6 +1409,7 @@
|
|||
DD4640202AFF10F4002A5ECB /* WaypointForm.swift in Sources */,
|
||||
233E99C12D849D6000CC3A77 /* DistanceCompactWidget.swift in Sources */,
|
||||
DD769E0328D18BF1001A3F05 /* DeviceMetricsLog.swift in Sources */,
|
||||
108FFECD2DD4005600BFAA81 /* NodeInfoEntityToNodeInfo.swift in Sources */,
|
||||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DD15E4F32B8BA56E00654F61 /* PaxCounterConfig.swift in Sources */,
|
||||
DDDB445229F8ACF900EE2349 /* Date.swift in Sources */,
|
||||
|
|
@ -1404,6 +1421,7 @@
|
|||
DDC94FCE29CF55310082EA6E /* RtttlConfig.swift in Sources */,
|
||||
DD964FBD296E6B01007C176F /* EmojiOnlyTextField.swift in Sources */,
|
||||
DD8169FF272476C700F4AB02 /* LogDocument.swift in Sources */,
|
||||
BCD93CBA2D9E11A2006C9214 /* DisconnectNodeIntent.swift in Sources */,
|
||||
DDC94FC129CE063B0082EA6E /* BatteryLevel.swift in Sources */,
|
||||
231B3F252D087C3C0069A07D /* EnvironmentDefaultColumns.swift in Sources */,
|
||||
25F5D5BE2C3F6D87008036E3 /* NavigationState.swift in Sources */,
|
||||
|
|
@ -1412,6 +1430,7 @@
|
|||
DDDB445429F8AD1600EE2349 /* Data.swift in Sources */,
|
||||
DDDB26462AACC0B7003AFCB7 /* NodeInfoItem.swift in Sources */,
|
||||
DDE5B4042B2279A700FCDD05 /* TraceRouteLog.swift in Sources */,
|
||||
237B46962DC8F1C100B22D99 /* RateLimitedButton.swift in Sources */,
|
||||
DD6193792863875F00E59241 /* SerialConfig.swift in Sources */,
|
||||
DDDB263F2AABEE20003AFCB7 /* NodeList.swift in Sources */,
|
||||
DDD5BB0B2C285E45007E03CA /* LogDetail.swift in Sources */,
|
||||
|
|
@ -1482,6 +1501,7 @@
|
|||
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */,
|
||||
DDD5BB092C285DDC007E03CA /* AppLog.swift in Sources */,
|
||||
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
|
||||
108FFECB2DD3F43C00BFAA81 /* ShareContactQRDialog.swift in Sources */,
|
||||
233E99C52D84A0B600CC3A77 /* CompactWidget.swift in Sources */,
|
||||
DDC1B81A2AB5377B00C71E39 /* MessagesTips.swift in Sources */,
|
||||
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */,
|
||||
|
|
@ -1539,6 +1559,7 @@
|
|||
2344A2AB2D66974300170A77 /* ManagedAttributePropertyWrapper.swift in Sources */,
|
||||
BCB613832C672A2600485544 /* MessageChannelIntent.swift in Sources */,
|
||||
D93068D52B812B700066FBC8 /* MessageDestination.swift in Sources */,
|
||||
BC10380F2DD4334400B00BFA /* AddContactIntent.swift in Sources */,
|
||||
DDA9515E2BC6F56F00CEA535 /* IndoorAirQuality.swift in Sources */,
|
||||
DDDB444E29F8AB0E00EE2349 /* Int.swift in Sources */,
|
||||
DD3CC6BC28E366DF00FA9159 /* Meshtastic.xcdatamodeld in Sources */,
|
||||
|
|
@ -1785,7 +1806,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.2;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1818,7 +1839,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.2;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
|
|
@ -1849,7 +1870,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.2;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1881,7 +1902,7 @@
|
|||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.6.2;
|
||||
MARKETING_VERSION = 2.6.3;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient.Widgets;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
@ -1980,6 +2001,7 @@
|
|||
DD3CC6BA28E366DF00FA9159 /* Meshtastic.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
DD63CB4E2DD4FBEA00AFCAE2 /* MeshtasticDataModelV 51.xcdatamodel */,
|
||||
233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */,
|
||||
8D3F8A3D2D44B137009EAAA4 /* MeshtasticDataModelV 49.xcdatamodel */,
|
||||
DDA28B1B2D32C89200EF726F /* MeshtasticDataModelV 48.xcdatamodel */,
|
||||
|
|
@ -2031,7 +2053,7 @@
|
|||
DD5D0A9A2931AD6B00F7EA61 /* MeshtasticDataModelV2.xcdatamodel */,
|
||||
DD3CC6BB28E366DF00FA9159 /* MeshtasticDataModel.xcdatamodel */,
|
||||
);
|
||||
currentVersion = 233E99B32D84969500CC3A77 /* MeshtasticDataModelV 50.xcdatamodel */;
|
||||
currentVersion = DD63CB4E2DD4FBEA00AFCAE2 /* MeshtasticDataModelV 51.xcdatamodel */;
|
||||
name = Meshtastic.xcdatamodeld;
|
||||
path = Meshtastic/Meshtastic.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
48
Meshtastic/AppIntents/AddContactIntent.swift
Normal file
48
Meshtastic/AppIntents/AddContactIntent.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// AddContactIntent.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Benjamin Faershtein on 5/13/25.
|
||||
//
|
||||
|
||||
import AppIntents
|
||||
import MeshtasticProtobufs
|
||||
|
||||
struct AddContactIntent: AppIntent {
|
||||
static var title: LocalizedStringResource = "Add Contact"
|
||||
static var description: IntentDescription = "Takes a Meshtastic contact URL and saves it to the nodes database"
|
||||
|
||||
@Parameter(title: "Contact URL", description: "The URL for the node to add")
|
||||
var contactUrl: URL
|
||||
|
||||
// Define the function that performs the main logic
|
||||
func perform() async throws -> some IntentResult {
|
||||
// Ensure the BLE Manager is connected
|
||||
if !BLEManager.shared.isConnected {
|
||||
throw AppIntentErrors.AppIntentError.notConnected
|
||||
}
|
||||
|
||||
if contactUrl.absoluteString.lowercased().contains("meshtastic.org/v/#") {
|
||||
let components = self.contactUrl.absoluteString.components(separatedBy: "#")
|
||||
// Extract contact information from the URL
|
||||
if let contactData = components.last {
|
||||
let decodedString = contactData.base64urlToBase64()
|
||||
if let decodedData = Data(base64Encoded: decodedString) {
|
||||
do {
|
||||
let success = BLEManager.shared.addContactFromURL(base64UrlString: contactData)
|
||||
if !success {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to add contact")
|
||||
}
|
||||
|
||||
} catch {
|
||||
throw AppIntentErrors.AppIntentError.message("Failed to parse contact data: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return a success result
|
||||
return .result()
|
||||
} else {
|
||||
throw AppIntentErrors.AppIntentError.message("The URL is not a valid Meshtastic contact link")
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Meshtastic/AppIntents/DisconnectNodeIntent.swift
Normal file
30
Meshtastic/AppIntents/DisconnectNodeIntent.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// DisconnectNodeIntent.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Benjamin Faershtein on 4/2/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppIntents
|
||||
|
||||
struct DisconnectNodeIntent: AppIntent {
|
||||
static var title: LocalizedStringResource = "Disconnect Node"
|
||||
|
||||
static var description: IntentDescription = "Disconnect the currently connected node"
|
||||
|
||||
func perform() async throws -> some IntentResult {
|
||||
if !BLEManager.shared.isConnected {
|
||||
throw AppIntentErrors.AppIntentError.notConnected
|
||||
}
|
||||
|
||||
if let connectedPeripheral = BLEManager.shared.connectedPeripheral,
|
||||
connectedPeripheral.peripheral.state == .connected {
|
||||
BLEManager.shared.disconnectPeripheral(reconnect: false)
|
||||
} else {
|
||||
throw AppIntentErrors.AppIntentError.message("Error disconnecting node")
|
||||
}
|
||||
|
||||
return .result()
|
||||
}
|
||||
}
|
||||
|
|
@ -32,5 +32,12 @@ struct ShortcutsProvider: AppShortcutsProvider {
|
|||
"Send a \(.applicationName) group message"],
|
||||
shortTitle: "Group Message",
|
||||
systemImageName: "message")
|
||||
AppShortcut(intent: DisconnectNodeIntent(),
|
||||
phrases: ["Disconnect \(.applicationName) node",
|
||||
"Disconnect my \(.applicationName) node",
|
||||
"Disconnect from \(.applicationName)",
|
||||
"Disconnect \(.applicationName)"],
|
||||
shortTitle: "Disconnect",
|
||||
systemImageName: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"symbols" : [
|
||||
{
|
||||
"filename" : "progress.ring.dashed.svg",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 341-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 3300 2200">
|
||||
<!--glyph: "", point size: 100.0, font version: "20.0d10e1", template writer version: "138.0.0"-->
|
||||
<style>.defaults {-sfsymbols-wiggle-style:clockwise;-sfsymbols-rotates-clockwise:true}
|
||||
|
||||
.monochrome-0 {-sfsymbols-variable-threshold:0.93;-sfsymbols-motion-group:12;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-1 {-sfsymbols-variable-threshold:0.86;-sfsymbols-motion-group:11;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-2 {-sfsymbols-variable-threshold:0.78;-sfsymbols-motion-group:10;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-3 {-sfsymbols-variable-threshold:0.7;-sfsymbols-motion-group:9;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-4 {-sfsymbols-variable-threshold:0.63;-sfsymbols-motion-group:8;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-5 {-sfsymbols-variable-threshold:0.55;-sfsymbols-motion-group:7;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-6 {-sfsymbols-variable-threshold:0.47;-sfsymbols-motion-group:6;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-7 {-sfsymbols-variable-threshold:0.39;-sfsymbols-motion-group:5;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-8 {-sfsymbols-variable-threshold:0.32;-sfsymbols-motion-group:4;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-9 {-sfsymbols-variable-threshold:0.24;-sfsymbols-motion-group:3;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-10 {-sfsymbols-variable-threshold:0.16;-sfsymbols-motion-group:2;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.monochrome-11 {-sfsymbols-variable-threshold:0.09;-sfsymbols-motion-group:1;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
|
||||
.multicolor-0:tintColor {-sfsymbols-variable-threshold:0.93;-sfsymbols-motion-group:12;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-1:tintColor {-sfsymbols-variable-threshold:0.86;-sfsymbols-motion-group:11;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-2:tintColor {-sfsymbols-variable-threshold:0.78;-sfsymbols-motion-group:10;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-3:tintColor {-sfsymbols-clear-behind:true;-sfsymbols-variable-threshold:0.7;-sfsymbols-motion-group:9;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-4:tintColor {-sfsymbols-variable-threshold:0.63;-sfsymbols-motion-group:8;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-5:tintColor {-sfsymbols-variable-threshold:0.55;-sfsymbols-motion-group:7;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-6:tintColor {-sfsymbols-variable-threshold:0.47;-sfsymbols-motion-group:6;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-7:tintColor {-sfsymbols-variable-threshold:0.39;-sfsymbols-motion-group:5;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-8:tintColor {-sfsymbols-variable-threshold:0.32;-sfsymbols-motion-group:4;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-9:tintColor {-sfsymbols-variable-threshold:0.24;-sfsymbols-motion-group:3;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-10:tintColor {-sfsymbols-variable-threshold:0.16;-sfsymbols-motion-group:2;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.multicolor-11:tintColor {-sfsymbols-variable-threshold:0.09;-sfsymbols-motion-group:1;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
|
||||
.hierarchical-0:primary {-sfsymbols-variable-threshold:0.93;-sfsymbols-motion-group:12;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-1:primary {-sfsymbols-variable-threshold:0.86;-sfsymbols-motion-group:11;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-2:primary {-sfsymbols-variable-threshold:0.78;-sfsymbols-motion-group:10;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-3:primary {-sfsymbols-clear-behind:true;-sfsymbols-variable-threshold:0.7;-sfsymbols-motion-group:9;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-4:secondary {-sfsymbols-variable-threshold:0.63;-sfsymbols-motion-group:8;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-5:tertiary {-sfsymbols-variable-threshold:0.55;-sfsymbols-motion-group:7;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-6:primary {-sfsymbols-variable-threshold:0.47;-sfsymbols-motion-group:6;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-7:primary {-sfsymbols-variable-threshold:0.39;-sfsymbols-motion-group:5;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-8:primary {-sfsymbols-variable-threshold:0.32;-sfsymbols-motion-group:4;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-9:primary {-sfsymbols-variable-threshold:0.24;-sfsymbols-motion-group:3;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-10:primary {-sfsymbols-variable-threshold:0.16;-sfsymbols-motion-group:2;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
.hierarchical-11:primary {-sfsymbols-variable-threshold:0.09;-sfsymbols-motion-group:1;-sfsymbols-layer-tags:-4ed3e2b5f369b4bf}
|
||||
|
||||
.SFSymbolsPreviewWireframe {fill:none;opacity:1.0;stroke:black;stroke-width:0.5}
|
||||
</style>
|
||||
<g id="Notes">
|
||||
<rect height="2200" id="artboard" style="fill:white;opacity:1" width="3300" x="0" y="0"/>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="292" y2="292"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 322)">Weight/Scale Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 559.711 322)">Ultralight</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 856.422 322)">Thin</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1153.13 322)">Light</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1449.84 322)">Regular</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 1746.56 322)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2043.27 322)">Semibold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2339.98 322)">Bold</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2636.69 322)">Heavy</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:middle;" transform="matrix(1 0 0 1 2933.4 322)">Black</text>
|
||||
<line style="fill:none;stroke:black;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1903" y2="1903"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 263 1933)">
|
||||
<path d="m46.2402 4.15039c21.7773 0 39.4531-17.627 39.4531-39.4043s-17.6758-39.4043-39.4531-39.4043c-21.7285 0-39.4043 17.627-39.4043 39.4043s17.6758 39.4043 39.4043 39.4043Zm0-7.42188c-17.6758 0-31.9336-14.3066-31.9336-31.9824s14.2578-31.9824 31.9336-31.9824 31.9824 14.3066 31.9824 31.9824-14.3066 31.9824-31.9824 31.9824Zm-17.9688-31.9824c0 2.14844 1.51367 3.61328 3.75977 3.61328h10.498v10.5957c0 2.19727 1.46484 3.71094 3.61328 3.71094 2.24609 0 3.71094-1.51367 3.71094-3.71094v-10.5957h10.5957c2.19727 0 3.71094-1.46484 3.71094-3.61328 0-2.19727-1.51367-3.71094-3.71094-3.71094h-10.5957v-10.5469c0-2.24609-1.46484-3.75977-3.71094-3.75977-2.14844 0-3.61328 1.51367-3.61328 3.75977v10.5469h-10.498c-2.24609 0-3.75977 1.51367-3.75977 3.71094Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 281.506 1933)">
|
||||
<path d="m58.5449 14.5508c27.4902 0 49.8047-22.3145 49.8047-49.8047s-22.3145-49.8047-49.8047-49.8047-49.8047 22.3145-49.8047 49.8047 22.3145 49.8047 49.8047 49.8047Zm0-8.30078c-22.9492 0-41.5039-18.5547-41.5039-41.5039s18.5547-41.5039 41.5039-41.5039 41.5039 18.5547 41.5039 41.5039-18.5547 41.5039-41.5039 41.5039Zm-22.6562-41.5039c0 2.39258 1.66016 4.00391 4.15039 4.00391h14.3555v14.4043c0 2.44141 1.66016 4.15039 4.05273 4.15039 2.44141 0 4.15039-1.66016 4.15039-4.15039v-14.4043h14.4043c2.44141 0 4.15039-1.61133 4.15039-4.00391 0-2.44141-1.70898-4.15039-4.15039-4.15039h-14.4043v-14.3555c0-2.49023-1.70898-4.19922-4.15039-4.19922-2.39258 0-4.05273 1.70898-4.05273 4.19922v14.3555h-14.3555c-2.49023 0-4.15039 1.70898-4.15039 4.15039Z"/>
|
||||
</g>
|
||||
<g transform="matrix(0.2 0 0 0.2 304.924 1933)">
|
||||
<path d="m74.8535 28.3203c35.1074 0 63.623-28.4668 63.623-63.5742s-28.5156-63.623-63.623-63.623-63.5742 28.5156-63.5742 63.623 28.4668 63.5742 63.5742 63.5742Zm0-9.08203c-30.127 0-54.4922-24.3652-54.4922-54.4922s24.3652-54.4922 54.4922-54.4922 54.4922 24.3652 54.4922 54.4922-24.3652 54.4922-54.4922 54.4922Zm-28.8574-54.4922c0 2.58789 1.85547 4.39453 4.58984 4.39453h19.7266v19.7754c0 2.68555 1.85547 4.58984 4.44336 4.58984 2.68555 0 4.54102-1.85547 4.54102-4.58984v-19.7754h19.7754c2.68555 0 4.58984-1.80664 4.58984-4.39453 0-2.73438-1.85547-4.58984-4.58984-4.58984h-19.7754v-19.7266c0-2.73438-1.85547-4.63867-4.54102-4.63867-2.58789 0-4.44336 1.9043-4.44336 4.63867v19.7266h-19.7266c-2.73438 0-4.58984 1.85547-4.58984 4.58984Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 263 1953)">Design Variations</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1971)">Symbols are supported in up to nine weights and three scales.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1989)">For optimal layout with text and other symbols, vertically align</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 2007)">symbols with the adjacent text.</text>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="776" x2="776" y1="1919" y2="1933"/>
|
||||
<g transform="matrix(0.2 0 0 0.2 776 1933)">
|
||||
<path d="m16.5527 0.78125c2.58789 0 3.85742-0.976562 4.78516-3.71094l6.29883-17.2363h28.8086l6.29883 17.2363c0.927734 2.73438 2.19727 3.71094 4.73633 3.71094 2.58789 0 4.24805-1.5625 4.24805-4.00391 0-0.830078-0.146484-1.61133-0.537109-2.63672l-22.9004-60.9863c-1.12305-2.97852-3.125-4.49219-6.25-4.49219-3.02734 0-5.07812 1.46484-6.15234 4.44336l-22.9004 61.084c-0.390625 1.02539-0.537109 1.80664-0.537109 2.63672 0 2.44141 1.5625 3.95508 4.10156 3.95508Zm13.4766-28.3691 11.8652-32.8613h0.244141l11.8652 32.8613Z"/>
|
||||
</g>
|
||||
<line style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="792.836" x2="792.836" y1="1919" y2="1933"/>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 776 1953)">Margins</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1971)">Leading and trailing margins on the left and right side of each symbol</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 1989)">can be adjusted by modifying the x-location of the margin guidelines.</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2007)">Modifications are automatically applied proportionally to all</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 776 2025)">scales and weights.</text>
|
||||
<g transform="matrix(0.2 0 0 0.2 1289 1933)">
|
||||
<path d="m14.209 9.32617 8.49609 8.54492c4.29688 4.3457 9.22852 4.05273 13.8672-1.07422l53.4668-58.9355-4.83398-4.88281-53.0762 58.3984c-1.75781 2.00195-3.41797 2.49023-5.76172 0.146484l-5.85938-5.81055c-2.34375-2.29492-1.80664-4.00391 0.195312-5.81055l57.373-54.0039-4.88281-4.83398-57.959 54.4434c-4.93164 4.58984-5.32227 9.47266-1.02539 13.8184Zm32.0801-90.9668c-2.09961 2.05078-2.24609 4.93164-1.07422 6.88477 1.17188 1.80664 3.4668 2.97852 6.68945 2.14844 7.32422-1.70898 14.9414-2.00195 22.0703 2.68555l-2.92969 7.27539c-1.70898 4.15039-0.830078 7.08008 1.85547 9.81445l11.4746 11.5723c2.44141 2.44141 4.49219 2.53906 7.32422 2.05078l5.32227-0.976562 3.32031 3.36914-0.195312 2.7832c-0.195312 2.49023 0.439453 4.39453 2.88086 6.78711l3.80859 3.71094c2.39258 2.39258 5.46875 2.53906 7.8125 0.195312l14.5508-14.5996c2.34375-2.34375 2.24609-5.32227-0.146484-7.71484l-3.85742-3.80859c-2.39258-2.39258-4.24805-3.17383-6.64062-2.97852l-2.88086 0.244141-3.22266-3.17383 1.2207-5.61523c0.634766-2.83203-0.146484-5.0293-3.07617-7.95898l-10.9863-10.9375c-16.6992-16.6016-38.8672-16.2109-53.3203-1.75781Zm7.4707 1.85547c12.1582-8.88672 28.6133-7.37305 39.7461 3.75977l12.1582 12.0605c1.17188 1.17188 1.36719 2.09961 1.02539 3.80859l-1.61133 7.42188 7.51953 7.42188 4.93164-0.292969c1.26953-0.0488281 1.66016 0.0488281 2.63672 1.02539l2.88086 2.88086-12.207 12.207-2.88086-2.88086c-0.976562-0.976562-1.12305-1.36719-1.07422-2.68555l0.341797-4.88281-7.4707-7.42188-7.61719 1.26953c-1.61133 0.341797-2.34375 0.195312-3.56445-0.976562l-10.0098-10.0098c-1.26953-1.17188-1.41602-2.00195-0.634766-3.85742l4.39453-10.4492c-7.8125-7.27539-17.9688-10.4004-28.125-7.42188-0.78125 0.195312-1.07422-0.439453-0.439453-0.976562Z"/>
|
||||
</g>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;font-weight:bold;" transform="matrix(1 0 0 1 1289 1953)">Exporting</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1971)">Symbols should be outlined when exporting to ensure the</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 1289 1989)">design is preserved when submitting to Xcode.</text>
|
||||
<text id="template-version" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1933)">Template v.6.0</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1951)">Requires Xcode 16 or greater</text>
|
||||
<text id="descriptive-name" style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1969)">Generated from progress.ring.dashed</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;text-anchor:end;" transform="matrix(1 0 0 1 3036 1987)">Typeset at 100.0 points</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 726)">Small</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1156)">Medium</text>
|
||||
<text style="stroke:none;fill:black;font-family:sans-serif;font-size:13;" transform="matrix(1 0 0 1 263 1586)">Large</text>
|
||||
</g>
|
||||
<g id="Guides">
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 696)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="696" y2="696"/>
|
||||
<line id="Capline-S" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="625.541" y2="625.541"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1126)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1126" y2="1126"/>
|
||||
<line id="Capline-M" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1055.54" y2="1055.54"/>
|
||||
<g id="H-reference" style="fill:#27AAE1;stroke:none;" transform="matrix(1 0 0 1 339 1556)">
|
||||
<path d="M0.993654 0L3.63775 0L29.3281-67.1323L30.0303-67.1323L30.0303-70.459L28.1226-70.459ZM11.6885-24.4799L46.9815-24.4799L46.2315-26.7285L12.4385-26.7285ZM55.1196 0L57.7637 0L30.6382-70.459L29.4326-70.459L29.4326-67.1323Z"/>
|
||||
</g>
|
||||
<line id="Baseline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1556" y2="1556"/>
|
||||
<line id="Capline-L" style="fill:none;stroke:#27AAE1;opacity:1;stroke-width:0.5;" x1="263" x2="3036" y1="1485.54" y2="1485.54"/>
|
||||
<line id="right-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2984.21" x2="2984.21" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Black-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="2882.59" x2="2882.59" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1499.82" x2="1499.82" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Regular-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="1399.87" x2="1399.87" y1="600.785" y2="720.121"/>
|
||||
<line id="right-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="607.643" x2="607.643" y1="600.785" y2="720.121"/>
|
||||
<line id="left-margin-Ultralight-S" style="fill:none;stroke:#00AEEF;stroke-width:0.5;opacity:1.0;" x1="511.779" x2="511.779" y1="600.785" y2="720.121"/>
|
||||
</g>
|
||||
<g id="Symbols">
|
||||
<g id="Black-S" transform="matrix(1 0 0 1 2882.59 696)">
|
||||
<path class="monochrome-0 multicolor-0:tintColor hierarchical-0:primary SFSymbolsPreviewWireframe" d="M40.5762-61.1816C42.5293-61.9141 44.5801-62.4512 46.7285-62.793C47.9492-62.9883 48.8281-63.916 48.8281-65.1855L48.8281-76.9043C48.8281-78.4668 47.6562-79.5898 46.0938-79.4434C41.4062-78.9551 36.9141-77.7344 32.7637-75.8301C31.3477-75.1953 30.9082-73.6328 31.6895-72.3145L37.5488-62.1582C38.1836-61.084 39.4043-60.6934 40.5762-61.1816Z"/>
|
||||
<path class="monochrome-1 multicolor-1:tintColor hierarchical-1:primary SFSymbolsPreviewWireframe" d="M29.0039-52.5879C30.3223-54.248 31.7871-55.7129 33.4473-57.0312C34.4727-57.8125 34.7656-59.082 34.1309-60.1562L28.2715-70.3125C27.4902-71.6797 25.8789-72.0703 24.6094-71.1426C20.8984-68.4082 17.627-65.1367 14.8926-61.4258C13.9648-60.1074 14.3555-58.5449 15.7227-57.7637L25.8789-51.9043C26.9531-51.2695 28.2227-51.5625 29.0039-52.5879Z"/>
|
||||
<path class="monochrome-2 multicolor-2:tintColor hierarchical-2:primary SFSymbolsPreviewWireframe" d="M23.291-39.3555C23.584-41.4551 24.1211-43.5059 24.9023-45.4102C25.3906-46.6309 25-47.8516 23.877-48.4863L13.7207-54.3457C12.4023-55.127 10.8398-54.6875 10.2051-53.2715C8.30078-49.1211 7.08008-44.6289 6.5918-39.9414C6.44531-38.3789 7.56836-37.207 9.13086-37.207L20.8984-37.207C22.168-37.207 23.0957-38.0859 23.291-39.3555Z"/>
|
||||
<path class="monochrome-3 multicolor-3:tintColor hierarchical-3:primary SFSymbolsPreviewWireframe" d="M24.8535-25.0977C24.1211-27.002 23.584-29.0039 23.291-31.1523C23.0957-32.4219 22.168-33.3008 20.8984-33.3008L9.13086-33.3008C7.56836-33.3008 6.44531-32.1289 6.5918-30.5664C7.08008-25.8789 8.30078-21.3867 10.2051-17.2363C10.8398-15.8203 12.4023-15.3809 13.7207-16.1621L23.877-22.0215C25-22.6562 25.3418-23.877 24.8535-25.0977Z"/>
|
||||
<path class="monochrome-4 multicolor-4:tintColor hierarchical-4:secondary SFSymbolsPreviewWireframe" d="M33.4473-13.4766C31.8359-14.7949 30.3223-16.2598 29.0039-17.9199C28.2227-18.9453 26.9531-19.2383 25.8789-18.6035L15.7227-12.7441C14.3555-11.9629 13.9648-10.4004 14.8926-9.08203C17.627-5.37109 20.8984-2.05078 24.6094 0.634766C25.8789 1.5625 27.4902 1.12305 28.2715-0.195312L34.1309-10.3516C34.7656-11.4258 34.4727-12.6953 33.4473-13.4766Z"/>
|
||||
<path class="monochrome-5 multicolor-5:tintColor hierarchical-5:tertiary SFSymbolsPreviewWireframe" d="M46.7285-7.71484C44.5801-8.05664 42.5293-8.59375 40.5762-9.32617C39.4043-9.81445 38.1836-9.42383 37.5488-8.34961L31.6895 1.80664C30.9082 3.125 31.3477 4.6875 32.7637 5.32227C36.9141 7.22656 41.4062 8.44727 46.0938 8.93555C47.6562 9.08203 48.8281 7.95898 48.8281 6.39648L48.8281-5.32227C48.8281-6.5918 47.9492-7.51953 46.7285-7.71484Z"/>
|
||||
<path class="monochrome-6 multicolor-6:tintColor hierarchical-6:primary SFSymbolsPreviewWireframe" d="M61.0352-9.32617C59.082-8.59375 57.0312-8.05664 54.8828-7.71484C53.6133-7.51953 52.7344-6.5918 52.7344-5.32227L52.7344 6.39648C52.7344 7.95898 53.9062 9.08203 55.4688 8.93555C60.1562 8.44727 64.6484 7.22656 68.8477 5.32227C70.2637 4.6875 70.7031 3.07617 69.9219 1.75781L64.0625-8.34961C63.4277-9.42383 62.207-9.81445 61.0352-9.32617Z"/>
|
||||
<path class="monochrome-7 multicolor-7:tintColor hierarchical-7:primary SFSymbolsPreviewWireframe" d="M72.6074-17.9199C71.2891-16.2598 69.8242-14.7949 68.1641-13.4766C67.1387-12.6953 66.8457-11.4258 67.4805-10.3516L73.3398-0.195312C74.1211 1.17188 75.7324 1.5625 77.002 0.634766C80.7129-2.09961 83.9844-5.37109 86.7188-9.13086C87.5977-10.4004 87.207-11.9629 85.8887-12.7441L75.7324-18.6035C74.6582-19.2383 73.3887-18.9453 72.6074-17.9199Z"/>
|
||||
<path class="monochrome-8 multicolor-8:tintColor hierarchical-8:primary SFSymbolsPreviewWireframe" d="M78.3203-31.1523C78.0273-29.0039 77.4902-27.002 76.709-25.0488C76.2207-23.877 76.6113-22.6562 77.6855-22.0215L87.8418-16.1621C89.1602-15.3809 90.7715-15.8203 91.4062-17.2852C93.2617-21.3867 94.4824-25.8789 94.9707-30.5664C95.1172-32.1289 93.9453-33.3008 92.4316-33.3008L80.7129-33.3008C79.4434-33.3008 78.5156-32.4219 78.3203-31.1523Z"/>
|
||||
<path class="monochrome-9 multicolor-9:tintColor hierarchical-9:primary SFSymbolsPreviewWireframe" d="M76.709-45.459C77.4902-43.5059 78.0273-41.5039 78.3203-39.3555C78.5156-38.0859 79.4434-37.207 80.7129-37.207L92.4316-37.207C93.9453-37.207 95.1172-38.3789 94.9707-39.9414C94.5312-44.5801 93.2617-49.0723 91.4062-53.2227C90.7715-54.6875 89.1602-55.127 87.8418-54.3457L77.6855-48.4863C76.6113-47.8516 76.2207-46.6309 76.709-45.459Z"/>
|
||||
<path class="monochrome-10 multicolor-10:tintColor hierarchical-10:primary SFSymbolsPreviewWireframe" d="M68.1641-57.0312C69.7754-55.7129 71.2891-54.248 72.6074-52.5879C73.3887-51.5625 74.6582-51.2695 75.7324-51.9043L85.8887-57.7637C87.207-58.5449 87.5977-60.1074 86.7188-61.377C83.9844-65.1367 80.7129-68.457 77.002-71.1426C75.7324-72.0703 74.1211-71.6309 73.3398-70.3125L67.4805-60.1562C66.8457-59.082 67.1387-57.8125 68.1641-57.0312Z"/>
|
||||
<path class="monochrome-11 multicolor-11:tintColor hierarchical-11:primary SFSymbolsPreviewWireframe" d="M54.834-62.793C57.0312-62.4512 59.082-61.9141 61.0352-61.1816C62.207-60.6934 63.4277-61.084 64.0625-62.1582L69.9219-72.3145C70.7031-73.6328 70.2637-75.1953 68.8477-75.8301C64.6973-77.7344 60.2051-78.9551 55.4688-79.4434C53.9062-79.5898 52.7344-78.4668 52.7344-76.9043L52.7344-65.1855C52.7344-63.916 53.6133-62.9883 54.834-62.793Z"/>
|
||||
</g>
|
||||
<g id="Regular-S" transform="matrix(1 0 0 1 1399.87 696)">
|
||||
<path class="monochrome-0 multicolor-0:tintColor hierarchical-0:primary SFSymbolsPreviewWireframe" d="M38.623-61.4746C41.1621-62.5977 43.8477-63.3301 46.7285-63.6719C47.5098-63.7207 48.0469-64.3066 48.0469-65.0879L48.0469-76.709C48.0469-77.6367 47.3633-78.3691 46.4355-78.2715C41.1621-77.832 36.1816-76.3672 31.5918-74.2188C30.7617-73.8281 30.5176-72.8516 30.957-72.0703L36.6699-62.1094C37.1094-61.4258 37.8906-61.1816 38.623-61.4746Z"/>
|
||||
<path class="monochrome-1 multicolor-1:tintColor hierarchical-1:primary SFSymbolsPreviewWireframe" d="M27.0508-52.2949C28.7109-54.541 30.6641-56.543 32.959-58.2031C33.5938-58.6914 33.7402-59.4727 33.3496-60.1562L27.5879-70.1172C27.1484-70.8984 26.1719-71.1426 25.3906-70.6055C21.1914-67.627 17.5781-64.0137 14.6484-59.8145C14.1113-59.0332 14.3066-58.0566 15.1367-57.5684L25.1465-51.8555C25.8301-51.4648 26.5625-51.6113 27.0508-52.2949Z"/>
|
||||
<path class="monochrome-2 multicolor-2:tintColor hierarchical-2:primary SFSymbolsPreviewWireframe" d="M21.582-38.5254C21.9238-41.3574 22.6562-44.0918 23.7305-46.582C24.0723-47.3145 23.8281-48.0957 23.1445-48.4863L13.1348-54.248C12.3047-54.7363 11.377-54.4434 10.9863-53.6133C8.83789-49.0234 7.42188-44.0918 6.98242-38.8184C6.93359-37.8906 7.56836-37.207 8.54492-37.207L20.166-37.207C20.9473-37.207 21.5332-37.7441 21.582-38.5254Z"/>
|
||||
<path class="monochrome-3 multicolor-3:tintColor hierarchical-3:primary SFSymbolsPreviewWireframe" d="M23.7305-23.877C22.6562-26.3672 21.9238-29.1016 21.582-31.9824C21.5332-32.7637 20.9473-33.3008 20.166-33.3008L8.49609-33.3008C7.56836-33.3008 6.88477-32.6172 6.98242-31.6895C7.42188-26.3672 8.83789-21.3867 11.0352-16.7969C11.4258-15.9668 12.3047-15.6738 13.1348-16.1621L23.1445-21.9727C23.8281-22.3633 24.0723-23.1445 23.7305-23.877Z"/>
|
||||
<path class="monochrome-4 multicolor-4:tintColor hierarchical-4:secondary SFSymbolsPreviewWireframe" d="M32.959-12.2559C30.7129-13.916 28.7109-15.8691 27.0508-18.1641C26.5625-18.7988 25.7812-18.9941 25.0977-18.5547L15.1367-12.8906C14.3066-12.4023 14.1113-11.4258 14.5996-10.6445C17.5781-6.44531 21.1914-2.83203 25.4395 0.146484C26.2207 0.683594 27.1973 0.439453 27.6367-0.341797L33.3496-10.3516C33.7402-11.0352 33.5938-11.7676 32.959-12.2559Z"/>
|
||||
<path class="monochrome-5 multicolor-5:tintColor hierarchical-5:tertiary SFSymbolsPreviewWireframe" d="M46.7285-6.78711C43.8965-7.12891 41.1621-7.86133 38.6719-8.93555C37.9395-9.27734 37.1582-9.0332 36.7188-8.34961L31.0059 1.61133C30.5664 2.39258 30.8105 3.36914 31.6406 3.75977C36.1816 5.9082 41.1621 7.37305 46.4355 7.8125C47.3633 7.86133 48.0469 7.17773 48.0469 6.25L48.0469-5.37109C48.0469-6.15234 47.5098-6.73828 46.7285-6.78711Z"/>
|
||||
<path class="monochrome-6 multicolor-6:tintColor hierarchical-6:primary SFSymbolsPreviewWireframe" d="M61.377-8.93555C58.8867-7.86133 56.1523-7.12891 53.2715-6.78711C52.4414-6.73828 51.9043-6.15234 51.9043-5.37109L51.9043 6.25C51.9043 7.22656 52.5879 7.91016 53.5645 7.8125C58.7891 7.37305 63.8672 5.9082 68.4082 3.75977C69.2383 3.36914 69.4824 2.39258 69.043 1.61133L63.2812-8.34961C62.8906-9.0332 62.1094-9.27734 61.377-8.93555Z"/>
|
||||
<path class="monochrome-7 multicolor-7:tintColor hierarchical-7:primary SFSymbolsPreviewWireframe" d="M72.998-18.1641C71.3379-15.918 69.2871-13.9648 67.041-12.2559C66.4062-11.7676 66.2109-10.9863 66.6016-10.3516L72.3633-0.390625C72.8027 0.390625 73.7793 0.683594 74.5605 0.146484C78.8086-2.83203 82.4219-6.49414 85.3516-10.6445C85.8887-11.4258 85.6445-12.4023 84.8633-12.8906L74.9023-18.6035C74.2676-19.043 73.4863-18.7988 72.998-18.1641Z"/>
|
||||
<path class="monochrome-8 multicolor-8:tintColor hierarchical-8:primary SFSymbolsPreviewWireframe" d="M78.418-31.9824C78.0762-29.1016 77.3438-26.3672 76.2207-23.877C75.9277-23.1445 76.1719-22.4121 76.8555-22.0215L86.8164-16.2598C87.6465-15.7715 88.623-16.0156 89.0137-16.8945C91.1621-21.4844 92.5293-26.416 92.9688-31.6895C93.0176-32.6172 92.3828-33.3008 91.4551-33.3008L79.8828-33.3008C79.1016-33.3008 78.4668-32.7637 78.418-31.9824Z"/>
|
||||
<path class="monochrome-9 multicolor-9:tintColor hierarchical-9:primary SFSymbolsPreviewWireframe" d="M76.2207-46.6309C77.2949-44.0918 78.0762-41.3574 78.418-38.5254C78.4668-37.7441 79.1016-37.207 79.8828-37.207L91.4551-37.207C92.3828-37.207 93.0176-37.8906 92.9688-38.8184C92.5293-44.043 91.1621-49.0723 89.0137-53.6133C88.623-54.4434 87.5977-54.7363 86.8164-54.248L76.8066-48.4375C76.1719-48.0469 75.9277-47.3145 76.2207-46.6309Z"/>
|
||||
<path class="monochrome-10 multicolor-10:tintColor hierarchical-10:primary SFSymbolsPreviewWireframe" d="M67.041-58.2031C69.2871-56.543 71.2891-54.5898 72.998-52.2949C73.4863-51.6602 74.2676-51.416 74.9023-51.8555L84.8633-57.5684C85.6934-58.0566 85.8887-59.0332 85.4004-59.8145C82.4219-64.0137 78.7598-67.6758 74.5605-70.6055C73.7793-71.1426 72.8027-70.8984 72.3633-70.1172L66.6016-60.1562C66.2109-59.4727 66.3574-58.6914 67.041-58.2031Z"/>
|
||||
<path class="monochrome-11 multicolor-11:tintColor hierarchical-11:primary SFSymbolsPreviewWireframe" d="M53.2715-63.6719C56.1523-63.3789 58.8379-62.5977 61.377-61.4746C62.1094-61.1816 62.8906-61.4258 63.2812-62.1094L69.043-72.0703C69.4824-72.8516 69.1895-73.8281 68.3594-74.2188C63.7695-76.3672 58.8379-77.832 53.5645-78.2715C52.6367-78.3203 51.9043-77.6367 51.9043-76.709L51.9043-65.0879C51.9043-64.3066 52.4414-63.7207 53.2715-63.6719Z"/>
|
||||
</g>
|
||||
<g id="Ultralight-S" transform="matrix(1 0 0 1 511.779 696)">
|
||||
<path class="monochrome-0 multicolor-0:tintColor hierarchical-0:primary SFSymbolsPreviewWireframe" d="M36.5342-61.1113C39.1187-62.2344 41.895-63.0122 44.7759-63.354C45.6934-63.4482 46.3213-64.1704 46.3213-65.0425L46.3213-74.4839C46.3213-75.5479 45.5469-76.3257 44.4829-76.228C39.4365-75.834 34.6831-74.5054 30.3657-72.4024C29.3994-71.9663 29.1099-70.8989 29.6401-69.9815L34.354-61.7915C34.7935-61.0171 35.6655-60.773 36.5342-61.1113Z"/>
|
||||
<path class="monochrome-1 multicolor-1:tintColor hierarchical-1:primary SFSymbolsPreviewWireframe" d="M25.0982-51.9771C26.8037-54.2686 28.8477-56.3159 31.1426-58.0669C31.9136-58.6006 32.1055-59.5181 31.6694-60.2925L26.9522-68.437C26.4219-69.3545 25.3545-69.5986 24.4824-69.0161C20.4194-66.2192 16.9424-62.6514 14.1489-58.6338C13.5664-57.7617 13.8071-56.6943 14.728-56.1607L22.876-51.4468C23.6504-50.9653 24.5645-51.2026 25.0982-51.9771Z"/>
|
||||
<path class="monochrome-2 multicolor-2:tintColor hierarchical-2:primary SFSymbolsPreviewWireframe" d="M19.7656-38.3438C20.062-41.2666 20.8399-44.001 22.0049-46.582C22.3467-47.4053 22.1026-48.2773 21.2827-48.7588L13.1348-53.4761C12.2139-54.0098 11.1499-53.6714 10.7139-52.7051C8.70166-48.3877 7.37647-43.6831 6.93701-38.6821C6.88818-37.6182 7.65918-36.7983 8.72656-36.7983L18.1226-36.7983C19.0401-36.7983 19.6714-37.4263 19.7656-38.3438Z"/>
|
||||
<path class="monochrome-3 multicolor-3:tintColor hierarchical-3:primary SFSymbolsPreviewWireframe" d="M22.0049-23.8315C20.8853-26.4126 20.1528-29.1924 19.811-32.1187C19.7168-33.0361 18.9946-33.6641 18.1226-33.6641L8.67773-33.6641C7.61377-33.6641 6.83936-32.8896 6.93701-31.8257C7.33106-26.7759 8.65625-22.0225 10.7173-17.6597C11.1533-16.6934 12.2593-16.355 13.1802-16.8887L21.3281-21.6548C22.148-22.1362 22.3921-23.0083 22.0049-23.8315Z"/>
|
||||
<path class="monochrome-4 multicolor-4:tintColor hierarchical-4:secondary SFSymbolsPreviewWireframe" d="M31.188-12.3921C28.8965-14.0977 26.8491-16.1416 25.1436-18.4365C24.6099-19.1621 23.7378-19.4028 22.918-18.918L14.7734-14.2529C13.8525-13.7192 13.5664-12.6518 14.1455-11.7798C16.9424-7.7622 20.4648-4.28514 24.4858-1.44286C25.3579-0.81493 26.4253-1.05907 26.9556-2.02196L31.6694-10.1699C32.1055-10.9443 31.9136-11.8584 31.188-12.3921Z"/>
|
||||
<path class="monochrome-5 multicolor-5:tintColor hierarchical-5:tertiary SFSymbolsPreviewWireframe" d="M44.7759-7.10498C41.8985-7.40137 39.1641-8.1792 36.583-9.29883C35.7598-9.68603 34.8423-9.39648 34.4029-8.62207L29.689-0.432113C29.1587 0.485367 29.4483 1.55275 30.4146 1.98878C34.6831 4.0464 39.4365 5.37502 44.4375 5.81447C45.5015 5.8633 46.3213 5.08888 46.3213 4.02492L46.3213-5.4165C46.3213-6.28857 45.6934-7.01074 44.7759-7.10498Z"/>
|
||||
<path class="monochrome-6 multicolor-6:tintColor hierarchical-6:primary SFSymbolsPreviewWireframe" d="M59.2881-9.34424C56.7525-8.22461 53.9727-7.44677 51.0464-7.10498C50.1255-7.01074 49.4976-6.28857 49.4976-5.4165L49.4976 4.07033C49.4976 5.13771 50.272 5.91213 51.3394 5.81447C56.3823 5.42043 61.1426 4.09181 65.502 1.98878C66.4683 1.55275 66.7578 0.485367 66.2276-0.432113L61.4649-8.62207C61.0288-9.39648 60.1113-9.68603 59.2881-9.34424Z"/>
|
||||
<path class="monochrome-7 multicolor-7:tintColor hierarchical-7:primary SFSymbolsPreviewWireframe" d="M70.7276-18.4819C69.022-16.1904 66.9712-14.1465 64.6343-12.3921C63.9087-11.8584 63.7134-10.9409 64.1494-10.1699L68.9121-2.02538C69.4424-1.06249 70.5098-0.81493 71.3819-1.44286C75.4483-4.23974 78.9253-7.76561 81.7188-11.7798C82.3013-12.6518 82.0572-13.7192 81.1397-14.2529L72.9497-18.9668C72.1333-19.4517 71.2612-19.2075 70.7276-18.4819Z"/>
|
||||
<path class="monochrome-8 multicolor-8:tintColor hierarchical-8:primary SFSymbolsPreviewWireframe" d="M76.0567-32.1187C75.7603-29.1924 74.9824-26.4126 73.814-23.8315C73.4756-23.0083 73.7197-22.1397 74.5396-21.6582L82.6841-16.9409C83.605-16.4072 84.6724-16.6968 85.1084-17.6665C87.166-21.9839 88.4878-26.7339 88.9273-31.7803C88.9761-32.8442 88.2051-33.6641 87.1411-33.6641L77.7031-33.6641C76.7857-33.6641 76.1509-33.0361 76.0567-32.1187Z"/>
|
||||
<path class="monochrome-9 multicolor-9:tintColor hierarchical-9:primary SFSymbolsPreviewWireframe" d="M73.814-46.5854C74.9336-44.001 75.7149-41.2212 76.0112-38.3438C76.1509-37.4263 76.7857-36.7983 77.7031-36.7983L87.1866-36.7983C88.2505-36.7983 89.0215-37.5728 88.9273-38.6367C88.5332-43.6797 87.2115-48.4365 85.1084-52.7959C84.6724-53.7622 83.6016-54.0552 82.6841-53.5215L74.4907-48.7554C73.6743-48.2739 73.4756-47.4507 73.814-46.5854Z"/>
|
||||
<path class="monochrome-10 multicolor-10:tintColor hierarchical-10:primary SFSymbolsPreviewWireframe" d="M64.6797-58.0215C66.9712-56.3159 69.0186-54.272 70.7276-51.9771C71.2612-51.2515 72.1333-51.0073 72.9497-51.4922L81.0943-56.1607C82.0152-56.6943 82.3013-57.7617 81.7222-58.6338C78.9707-62.6514 75.4449-66.1318 71.4273-68.9707C70.5552-69.5986 69.4424-69.3545 68.9121-68.3916L64.1948-60.2471C63.7588-59.4727 63.9507-58.5552 64.6797-58.0215Z"/>
|
||||
<path class="monochrome-11 multicolor-11:tintColor hierarchical-11:primary SFSymbolsPreviewWireframe" d="M51.0464-63.354C53.9727-63.061 56.7036-62.2798 59.2881-61.1113C60.1113-60.773 61.0288-61.0625 61.4649-61.8369L66.2276-69.9815C66.7578-70.8989 66.4195-71.9663 65.4531-72.4024C61.1358-74.46 56.3858-75.7886 51.3848-76.228C50.3208-76.2769 49.4976-75.5025 49.4976-74.4385L49.4976-65.0425C49.4976-64.1704 50.1255-63.4482 51.0464-63.354Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -37,17 +37,17 @@ enum ActivityType: Int, CaseIterable, Identifiable {
|
|||
var fileNameString: String {
|
||||
switch self {
|
||||
case .walking:
|
||||
return "walk".localized
|
||||
return "Walking".localized.lowercased()
|
||||
case .hiking:
|
||||
return "hiking".localized
|
||||
return "Hiking".localized.lowercased()
|
||||
case .biking:
|
||||
return "biking".localized
|
||||
return "Biking".localized.lowercased()
|
||||
case .driving:
|
||||
return "driving".localized
|
||||
return "Driving".localized.lowercased()
|
||||
case .overlanding:
|
||||
return "overlanding".localized
|
||||
return "Overlanding".localized.lowercased()
|
||||
case .skiing:
|
||||
return "skiing".localized
|
||||
return "Skiing".localized.lowercased()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,4 +31,11 @@ extension MessageEntity {
|
|||
|
||||
return (try? context.fetch(fetchRequest)) ?? [MessageEntity]()
|
||||
}
|
||||
|
||||
func displayTimestamp(aboveMessage: MessageEntity?) -> Bool {
|
||||
if let aboveMessage = aboveMessage {
|
||||
return aboveMessage.timestamp.addingTimeInterval(3600) < timestamp // 60 minutes
|
||||
}
|
||||
return false // First message will have no timestamp
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// NodeInfoEntityToNodeInfo.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Utility to convert NodeInfoEntity (Core Data) to NodeInfo (protobuf)
|
||||
|
||||
import Foundation
|
||||
import MeshtasticProtobufs
|
||||
|
||||
extension NodeInfoEntity {
|
||||
func toProto() -> NodeInfo {
|
||||
var userProto = User()
|
||||
if let user = self.user {
|
||||
userProto.id = user.userId ?? ""
|
||||
userProto.longName = user.longName ?? ""
|
||||
userProto.shortName = user.shortName ?? ""
|
||||
userProto.hwModel = HardwareModel(rawValue: Int(user.hwModelId)) ?? .unset
|
||||
userProto.isLicensed = user.isLicensed
|
||||
if userProto.hasIsUnmessagable == true {
|
||||
userProto.isUnmessagable = user.unmessagable
|
||||
}
|
||||
userProto.role = Config.DeviceConfig.Role(rawValue: Int(user.role)) ?? .client
|
||||
userProto.publicKey = user.publicKey?.subdata(in: 0..<user.publicKey!.count) ?? Data()
|
||||
}
|
||||
var node = NodeInfo()
|
||||
node.num = UInt32(self.num)
|
||||
node.user = userProto
|
||||
// Add more fields as needed
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
|
@ -115,6 +115,17 @@ extension String {
|
|||
.joined()
|
||||
}
|
||||
|
||||
/// Formats a short name like "P130" to read as "Node P 130" for VoiceOver
|
||||
/// This ensures proper pronunciation of alphanumeric node IDs
|
||||
func formatNodeNameForVoiceOver() -> String {
|
||||
let spaced = self.replacingOccurrences(
|
||||
of: #"([A-Za-z])([0-9]+)"#,
|
||||
with: "$1 $2",
|
||||
options: .regularExpression
|
||||
)
|
||||
return "Node".localized + " " + spaced
|
||||
}
|
||||
|
||||
// Adds variation selectors to prefer the graphical form of emoji.
|
||||
// Looks ahead to make sure that the variation selector is not already applied.
|
||||
var addingVariationSelectors: String {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ extension UserDefaults {
|
|||
case enableMapTraffic
|
||||
case enableMapPointsOfInterest
|
||||
case enableOfflineMaps
|
||||
case enableMapShowFavorites
|
||||
case mapTileServer
|
||||
case enableOverlayServer
|
||||
case mapOverlayServer
|
||||
|
|
@ -119,6 +120,9 @@ extension UserDefaults {
|
|||
@UserDefault(.enableMapPointsOfInterest, defaultValue: false)
|
||||
static var enableMapPointsOfInterest: Bool
|
||||
|
||||
@UserDefault(.enableMapShowFavorites, defaultValue: false)
|
||||
static var enableMapShowFavorites: Bool
|
||||
|
||||
@UserDefault(.enableDetectionNotifications, defaultValue: false)
|
||||
static var enableDetectionNotifications: Bool
|
||||
|
||||
|
|
|
|||
|
|
@ -177,20 +177,23 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
|
||||
// Disconnect Connected Peripheral
|
||||
func disconnectPeripheral(reconnect: Bool = true) {
|
||||
|
||||
guard let connectedPeripheral = connectedPeripheral else { return }
|
||||
if mqttProxyConnected {
|
||||
mqttManager.mqttClientProxy?.disconnect()
|
||||
// Ensure all operations run on the main thread
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard let connectedPeripheral = self.connectedPeripheral else { return }
|
||||
if self.mqttProxyConnected {
|
||||
self.mqttManager.mqttClientProxy?.disconnect()
|
||||
}
|
||||
self.automaticallyReconnect = reconnect
|
||||
self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
|
||||
self.FROMRADIO_characteristic = nil
|
||||
self.isConnected = false
|
||||
self.isSubscribed = false
|
||||
self.invalidVersion = false
|
||||
self.connectedVersion = "0.0.0"
|
||||
self.stopScanning()
|
||||
self.startScanning()
|
||||
}
|
||||
automaticallyReconnect = reconnect
|
||||
centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral)
|
||||
FROMRADIO_characteristic = nil
|
||||
isConnected = false
|
||||
isSubscribed = false
|
||||
invalidVersion = false
|
||||
connectedVersion = "0.0.0"
|
||||
stopScanning()
|
||||
startScanning()
|
||||
}
|
||||
|
||||
// Called each time a peripheral is connected
|
||||
|
|
@ -940,7 +943,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
subtitle: "TR received back from \(destinationHop.name ?? "unknown")",
|
||||
content: "Hops from: \(tr.hopsTowards), Hops back: \(tr.hopsBack)\n\(tr.routeText ?? "Unknown".localized)\n\(tr.routeBackText ?? "Unknown".localized)",
|
||||
target: "nodes",
|
||||
path: "meshtastic:///nodes?nodenum=\(connectedNode.user?.num ?? 0)"
|
||||
path: "meshtastic:///nodes?nodenum=\(tr.node?.num ?? 0)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
@ -1120,6 +1123,19 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
if newMessage.toUser?.pkiEncrypted ?? false {
|
||||
meshPacket.pkiEncrypted = true
|
||||
meshPacket.publicKey = newMessage.toUser?.publicKey ?? Data()
|
||||
// Auto Favorite nodes you DM so they don't roll out of the nodedb
|
||||
if !(newMessage.toUser?.userNode?.favorite ?? true) {
|
||||
newMessage.toUser?.userNode?.favorite = true
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Auto favorited node bases on sending a message \(self.connectedPeripheral.num.toHex(), privacy: .public) to \(toUserNum.toHex(), privacy: .public)")
|
||||
_ = self.setFavoriteNode(node: (newMessage.toUser?.userNode)!, connectedNodeNum: fromUserNum)
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Unresolved Core Data error when auto favoriting in Send Message Function. Error: \(nsError, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
meshPacket.id = UInt32(newMessage.messageId)
|
||||
if toUserNum > 0 {
|
||||
|
|
@ -1757,6 +1773,70 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
return false
|
||||
}
|
||||
|
||||
public func addContactFromURL(base64UrlString: String) -> Bool {
|
||||
if isConnected {
|
||||
|
||||
let decodedString = base64UrlString.base64urlToBase64()
|
||||
if let decodedData = Data(base64Encoded: decodedString) {
|
||||
do {
|
||||
let contact: SharedContact = try SharedContact(serializedBytes: decodedData)
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.addContact = contact
|
||||
var meshPacket: MeshPacket = MeshPacket()
|
||||
meshPacket.to = UInt32(connectedPeripheral.num)
|
||||
meshPacket.from = UInt32(connectedPeripheral.num)
|
||||
meshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
meshPacket.priority = MeshPacket.Priority.reliable
|
||||
meshPacket.wantAck = true
|
||||
meshPacket.channel = 0
|
||||
var dataMessage = DataMessage()
|
||||
guard let adminData: Data = try? adminPacket.serializedData() else {
|
||||
return false
|
||||
}
|
||||
dataMessage.payload = adminData
|
||||
dataMessage.portnum = PortNum.adminApp
|
||||
meshPacket.decoded = dataMessage
|
||||
var toRadio: ToRadio!
|
||||
toRadio = ToRadio()
|
||||
toRadio.packet = meshPacket
|
||||
guard let binaryData: Data = try? toRadio.serializedData() else {
|
||||
return false
|
||||
}
|
||||
|
||||
// Create a NodeInfo (User) packet for the newly added contact
|
||||
var dataNodeMessage = DataMessage()
|
||||
if let nodeInfoData = try? contact.user.serializedData() {
|
||||
dataNodeMessage.payload = nodeInfoData
|
||||
dataNodeMessage.portnum = PortNum.nodeinfoApp
|
||||
var nodeMeshPacket = MeshPacket()
|
||||
nodeMeshPacket.id = UInt32.random(in: UInt32(UInt8.max)..<UInt32.max)
|
||||
nodeMeshPacket.to = UInt32.max
|
||||
nodeMeshPacket.from = UInt32(contact.nodeNum)
|
||||
nodeMeshPacket.decoded = dataNodeMessage
|
||||
// Update local database with the new node info
|
||||
upsertNodeInfoPacket(packet: nodeMeshPacket, context: context)
|
||||
}
|
||||
|
||||
if connectedPeripheral?.peripheral.state ?? CBPeripheralState.disconnected == CBPeripheralState.connected {
|
||||
self.connectedPeripheral.peripheral.writeValue(binaryData, for: self.TORADIO_characteristic, type: .withResponse)
|
||||
let logString = String.localizedStringWithFormat("Added contact %@ to device".localized, contact.user.longName)
|
||||
Logger.mesh.info("📻 \(logString, privacy: .public)")
|
||||
}
|
||||
|
||||
if self.connectedPeripheral != nil {
|
||||
self.sendWantConfig()
|
||||
return true
|
||||
}
|
||||
|
||||
} catch {
|
||||
Logger.data.error("Failed to decode contact data: \(error.localizedDescription, privacy: .public)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public func saveUser(config: User, fromUser: UserEntity, toUser: UserEntity, adminIndex: Int32) -> Int64 {
|
||||
var adminPacket = AdminMessage()
|
||||
adminPacket.setOwner = config
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ class LocalNotificationManager {
|
|||
if notification.critical {
|
||||
content.sound = UNNotificationSound.defaultCritical
|
||||
}
|
||||
|
||||
let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: nil)
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
|
||||
let request = UNNotificationRequest(identifier: notification.id, content: content, trigger: trigger)
|
||||
|
||||
UNUserNotificationCenter.current().add(request) { error in
|
||||
if let error {
|
||||
|
|
|
|||
|
|
@ -109,15 +109,42 @@ import OSLog
|
|||
} else {
|
||||
locationsArray = [location]
|
||||
}
|
||||
UserDefaults.standard.set(location.coordinate.latitude, forKey: "lastKnownLatitude")
|
||||
UserDefaults.standard.set(location.coordinate.longitude, forKey: "lastKnownLongitude")
|
||||
UserDefaults.standard.set(Date().timeIntervalSince1970, forKey: "lastKnownLocationTimestamp")
|
||||
return true
|
||||
}
|
||||
|
||||
static let DefaultLocation = CLLocationCoordinate2D(latitude: 37.3346, longitude: -122.0090)
|
||||
static var currentLocation: CLLocationCoordinate2D {
|
||||
guard let location = shared.manager.location else {
|
||||
if let location = shared.manager.location {
|
||||
return location.coordinate
|
||||
} else {
|
||||
// Check authorization status
|
||||
let status = shared.manager.authorizationStatus
|
||||
switch status {
|
||||
case .notDetermined:
|
||||
Logger.services.info("📍 [App] Location permission not determined, requesting authorization")
|
||||
shared.manager.requestWhenInUseAuthorization()
|
||||
case .denied, .restricted:
|
||||
Logger.services.warning("📍 [App] Location access denied or restricted. Please enable location services in Settings to get accurate positioning!")
|
||||
shared.manager.requestWhenInUseAuthorization()
|
||||
default:
|
||||
break
|
||||
}
|
||||
// Fallback 1: Last known location from UserDefaults (if within 4 hours)
|
||||
if let lat = UserDefaults.standard.object(forKey: "lastKnownLatitude") as? Double,
|
||||
let lon = UserDefaults.standard.object(forKey: "lastKnownLongitude") as? Double,
|
||||
let timestamp = UserDefaults.standard.object(forKey: "lastKnownLocationTimestamp") as? Double,
|
||||
lat >= -90 && lat <= 90,
|
||||
lon >= -180 && lon <= 180,
|
||||
Date().timeIntervalSince1970 - timestamp <= 14_400 { // 4 hours in seconds
|
||||
Logger.services.info("📍 [App] Falling back to last known location (age: \(Int(Date().timeIntervalSince1970 - timestamp)) seconds)")
|
||||
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
|
||||
}
|
||||
// Fallback 2: Default location
|
||||
Logger.services.warning("📍 [App] No Location and no last known location, something is really wrong. Teleporting user to Apple Park")
|
||||
return DefaultLocation
|
||||
}
|
||||
return location.coordinate
|
||||
}
|
||||
|
||||
static var satsInView: Int {
|
||||
|
|
|
|||
|
|
@ -317,6 +317,17 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
newUser.pkiEncrypted = true
|
||||
newUser.publicKey = nodeInfo.user.publicKey
|
||||
}
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfo.user.hasIsUnmessagable {
|
||||
newUser.unmessagable = nodeInfo.user.isUnmessagable
|
||||
} else {
|
||||
let roles = [2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(newUser.role))
|
||||
if containsRole {
|
||||
newUser.unmessagable = true
|
||||
} else {
|
||||
newUser.unmessagable = false
|
||||
}}
|
||||
newNode.user = newUser
|
||||
} else if nodeInfo.num > Constants.minimumNodeNum {
|
||||
let newUser = createUser(num: Int64(nodeInfo.num), context: context)
|
||||
|
|
@ -380,19 +391,31 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje
|
|||
fetchedNode[0].user?.pkiEncrypted = true
|
||||
fetchedNode[0].user?.publicKey = nodeInfo.user.publicKey
|
||||
}
|
||||
fetchedNode[0].user!.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user!.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user!.numString = String(nodeInfo.num)
|
||||
fetchedNode[0].user!.longName = nodeInfo.user.longName
|
||||
fetchedNode[0].user!.shortName = nodeInfo.user.shortName
|
||||
fetchedNode[0].user!.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user!.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
fetchedNode[0].user?.userId = nodeInfo.user.id
|
||||
fetchedNode[0].user?.num = Int64(nodeInfo.num)
|
||||
fetchedNode[0].user?.numString = String(nodeInfo.num)
|
||||
fetchedNode[0].user?.longName = nodeInfo.user.longName
|
||||
fetchedNode[0].user?.shortName = nodeInfo.user.shortName
|
||||
fetchedNode[0].user?.isLicensed = nodeInfo.user.isLicensed
|
||||
fetchedNode[0].user?.role = Int32(nodeInfo.user.role.rawValue)
|
||||
fetchedNode[0].user?.hwModel = String(describing: nodeInfo.user.hwModel).uppercased()
|
||||
fetchedNode[0].user?.hwModelId = Int32(nodeInfo.user.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfo.user.hasIsUnmessagable {
|
||||
fetchedNode[0].user?.unmessagable = nodeInfo.user.isUnmessagable
|
||||
} else {
|
||||
let roles = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(fetchedNode[0].user?.role ?? -1))
|
||||
if containsRole {
|
||||
fetchedNode[0].user?.unmessagable = true
|
||||
} else {
|
||||
fetchedNode[0].user?.unmessagable = false
|
||||
}
|
||||
}
|
||||
Task {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
let dh = hw.first(where: { $0.hwModel == fetchedNode[0].user!.hwModelId })
|
||||
fetchedNode[0].user!.hwDisplayName = dh?.displayName
|
||||
fetchedNode[0].user?.hwDisplayName = dh?.displayName
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1040,17 +1063,29 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
let logString = String.localizedStringWithFormat("Waypoint Packet received from node: %@".localized, String(packet.from))
|
||||
Logger.mesh.info("📍 \(logString, privacy: .public)")
|
||||
|
||||
let fetchWaypointRequest = WaypointEntity.fetchRequest()
|
||||
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(packet.id))
|
||||
|
||||
do {
|
||||
|
||||
if let waypointMessage = try? Waypoint(serializedBytes: packet.decoded.payload) {
|
||||
let fetchedWaypoint = try context.fetch(fetchWaypointRequest)
|
||||
if fetchedWaypoint.isEmpty {
|
||||
let waypoint = WaypointEntity(context: context)
|
||||
// Fetch waypoint by waypointMessage.id, not packet.id
|
||||
let fetchWaypointRequest = WaypointEntity.fetchRequest()
|
||||
fetchWaypointRequest.predicate = NSPredicate(format: "id == %lld", Int64(waypointMessage.id))
|
||||
|
||||
waypoint.id = Int64(packet.id)
|
||||
let fetchedWaypoint = try context.fetch(fetchWaypointRequest)
|
||||
// Fetch the node info to get the short name
|
||||
var nodeShortName: String = "?"
|
||||
let fetchNodeRequest = NodeInfoEntity.fetchRequest()
|
||||
fetchNodeRequest.predicate = NSPredicate(format: "num == %lld", Int64(packet.from))
|
||||
do {
|
||||
let fetchedNode = try context.fetch(fetchNodeRequest)
|
||||
if let node = fetchedNode.first, let user = node.user {
|
||||
nodeShortName = user.shortName ?? node.user?.userId ?? String(packet.from.toHex())
|
||||
}
|
||||
} catch {
|
||||
Logger.data.error("Failed to fetch NodeInfoEntity for node \(packet.from.toHex(), privacy: .public): \(error)")
|
||||
}
|
||||
if fetchedWaypoint.isEmpty {
|
||||
// Create a new waypoint
|
||||
let waypoint = WaypointEntity(context: context)
|
||||
waypoint.id = Int64(waypointMessage.id) // Use waypointMessage.id
|
||||
waypoint.name = waypointMessage.name
|
||||
waypoint.longDescription = waypointMessage.description_p
|
||||
waypoint.latitudeI = waypointMessage.latitudeI
|
||||
|
|
@ -1073,7 +1108,7 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
manager.notifications = [
|
||||
Notification(
|
||||
id: ("notification.id.\(waypoint.id)"),
|
||||
title: "New Waypoint Received",
|
||||
title: "New Waypoint From \(nodeShortName)",
|
||||
subtitle: "\(icon) \(waypoint.name ?? "Dropped Pin")",
|
||||
content: "\(waypoint.longDescription ?? "\(latitude), \(longitude)")",
|
||||
target: "map",
|
||||
|
|
@ -1088,26 +1123,42 @@ func waypointPacket (packet: MeshPacket, context: NSManagedObjectContext) {
|
|||
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
fetchedWaypoint[0].id = Int64(packet.id)
|
||||
fetchedWaypoint[0].name = waypointMessage.name
|
||||
fetchedWaypoint[0].longDescription = waypointMessage.description_p
|
||||
fetchedWaypoint[0].latitudeI = waypointMessage.latitudeI
|
||||
fetchedWaypoint[0].longitudeI = waypointMessage.longitudeI
|
||||
fetchedWaypoint[0].icon = Int64(waypointMessage.icon)
|
||||
fetchedWaypoint[0].locked = Int64(waypointMessage.lockedTo)
|
||||
if waypointMessage.expire >= 1 {
|
||||
fetchedWaypoint[0].expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire)))
|
||||
} else {
|
||||
fetchedWaypoint[0].expire = nil
|
||||
}
|
||||
fetchedWaypoint[0].lastUpdated = Date()
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Updated Node Waypoint App Packet For: \(fetchedWaypoint[0].id, privacy: .public)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
|
||||
// Update existing waypoint
|
||||
let existingWaypoint = fetchedWaypoint[0]
|
||||
if existingWaypoint.locked == 0 || existingWaypoint.locked == packet.from {
|
||||
let currentTime = Int64(Date().timeIntervalSince1970)
|
||||
if waypointMessage.expire > 0 && waypointMessage.expire <= currentTime {
|
||||
context.delete(existingWaypoint)
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Deleted a waypoint")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
existingWaypoint.name = waypointMessage.name
|
||||
existingWaypoint.longDescription = waypointMessage.description_p
|
||||
existingWaypoint.latitudeI = waypointMessage.latitudeI
|
||||
existingWaypoint.longitudeI = waypointMessage.longitudeI
|
||||
existingWaypoint.icon = Int64(waypointMessage.icon)
|
||||
existingWaypoint.locked = Int64(waypointMessage.lockedTo)
|
||||
if waypointMessage.expire >= 1 {
|
||||
existingWaypoint.expire = Date(timeIntervalSince1970: TimeInterval(Int64(waypointMessage.expire)))
|
||||
} else {
|
||||
existingWaypoint.expire = nil
|
||||
}
|
||||
existingWaypoint.lastUpdated = Date()
|
||||
do {
|
||||
try context.save()
|
||||
Logger.data.info("💾 Updated Node Waypoint App Packet For: \(existingWaypoint.id, privacy: .public)")
|
||||
} catch {
|
||||
context.rollback()
|
||||
let nsError = error as NSError
|
||||
Logger.data.error("Error Saving WaypointEntity from WAYPOINT_APP \(nsError, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,31 +41,29 @@ class MqttClientProxyManager {
|
|||
|
||||
if let host = host {
|
||||
let port = defaultServerPort
|
||||
let username = node.mqttConfig?.username
|
||||
let password = node.mqttConfig?.password
|
||||
let root = node.mqttConfig?.root?.count ?? 0 > 0 ? node.mqttConfig?.root : "msh"
|
||||
let prefix = root!
|
||||
topic = prefix + "/2/e" + "/#"
|
||||
// Require opt in to map report terms to connect
|
||||
if node.mqttConfig?.mapReportingEnabled ?? false && UserDefaults.mapReportingOptIn || !(node.mqttConfig?.mapReportingEnabled ?? false) {
|
||||
connect(host: host, port: port, useSsl: useSsl, username: username, password: password, topic: topic)
|
||||
connect(host: host, port: port, useSsl: useSsl, topic: topic, node: node)
|
||||
} else {
|
||||
delegate?.onMqttError(message: "MQTT Map Reporting Terms need to be accepted.")
|
||||
}
|
||||
}
|
||||
}
|
||||
func connect(host: String, port: Int, useSsl: Bool, username: String?, password: String?, topic: String?) {
|
||||
func connect(host: String, port: Int, useSsl: Bool, topic: String?, node: NodeInfoEntity) {
|
||||
guard !host.isEmpty else {
|
||||
delegate?.onMqttDisconnected()
|
||||
return
|
||||
}
|
||||
let clientId = "MeshtasticAppleMqttProxy-" + String(ProcessInfo().processIdentifier)
|
||||
let clientId = "MeshtasticAppleMqttProxy-" + (node.user?.userId ?? String(ProcessInfo().processIdentifier))
|
||||
mqttClientProxy = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port))
|
||||
if let mqttClient = mqttClientProxy {
|
||||
mqttClient.enableSSL = useSsl
|
||||
mqttClient.allowUntrustCACertificate = true
|
||||
mqttClient.username = username
|
||||
mqttClient.password = password
|
||||
mqttClient.username = node.mqttConfig?.username
|
||||
mqttClient.password = node.mqttConfig?.password
|
||||
mqttClient.keepAlive = 60
|
||||
mqttClient.cleanSession = true
|
||||
if debugLog {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>applinks:meshtastic.org/e/*</string>
|
||||
<string>applinks:meshtastic.org/v/*</string>
|
||||
</array>
|
||||
<key>com.apple.developer.weatherkit</key>
|
||||
<true/>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>MeshtasticDataModelV 50.xcdatamodel</string>
|
||||
<string>MeshtasticDataModelV 51.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D81" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788" systemVersion="24D81" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,505 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23788" systemVersion="24D81" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="AmbientLightingConfigEntity" representedClassName="AmbientLightingConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="blue" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="green" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ledState" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="red" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="ambientLightingConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="BluetoothConfigEntity" representedClassName="BluetoothConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceLoggingEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPin" optional="YES" attributeType="Integer 32" defaultValueString="123456" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="bluetoothConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="bluetoothConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="CannedMessageConfigEntity" representedClassName="CannedMessageConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCcw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventCw" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerEventPress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinA" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinB" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="inputbrokerPinPress" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="messages" optional="YES" attributeType="String" minValueString="0" maxValueString="198"/>
|
||||
<attribute name="rotary1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="updown1Enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="cannedMessagesConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="cannedMessageConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ChannelEntity" representedClassName="ChannelEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="downlinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="index" attributeType="Integer 32" minValueString="0" maxValueString="13" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="psk" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uplinkEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="myInfoChannel" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="channels" inverseEntity="MyInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="index"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="DetectionSensorConfigEntity" representedClassName="DetectionSensorConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="minimumBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="monitorPin" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="sendBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="stateBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="triggerType" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="usePullup" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<relationship name="detectionSensorConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="detectionSensorConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceConfigEntity" representedClassName="DeviceConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="buttonGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="buzzerGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="disableTripleClick" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="doubleTapAsButtonPress" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="ledHeartbeatEnabled" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="nodeInfoBroadcastSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rebroadcastMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="serialEnabled" optional="YES" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="tripleClickAsAdHocPing" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="tzdef" optional="YES" attributeType="String"/>
|
||||
<relationship name="deviceConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="deviceConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DeviceMetadataEntity" representedClassName="DeviceMetadataEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="canShutdown" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceStateVersion" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="excludedModules" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasBluetooth" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasEthernet" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hasWifi" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="hwModel" optional="YES" attributeType="String"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="role" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="metadataNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="metadata" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="DisplayConfigEntity" representedClassName="DisplayConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="compassNorthTop" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="displayMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="flipScreen" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsFormat" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="headingBold" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="oledType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenCarouselInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="screenOnSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="units" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wakeOnTapOrMotion" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="displayConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="displayConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="ExternalNotificationConfigEntity" representedClassName="ExternalNotificationConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="active" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBell" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertBellVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessage" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="alertMessageVibra" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="nagTimeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="output" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputBuzzer" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputMilliseconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="outputVibra" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="useI2SAsBuzzer" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="usePWM" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="externalNotificationConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="externalNotificationConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="LocationEntity" representedClassName="LocationEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="routeLocation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RouteEntity" inverseName="locations" inverseEntity="RouteEntity"/>
|
||||
</entity>
|
||||
<entity name="LoRaConfigEntity" representedClassName="LoRaConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bandwidth" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="codingRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="frequencyOffset" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopLimit" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ignoreMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="modemPreset" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="okToMqtt" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideDutyCycle" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideFrequency" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="regionCode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="spreadFactor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sx126xRxBoostedGain" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="txPower" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="usePreset" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<relationship name="loRaConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="loRaConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MessageEntity" representedClassName="MessageEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ackError" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackSNR" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="ackTimestamp" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="admin" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isEmoji" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="messageId" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="messagePayload" optional="YES" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="messagePayloadMarkdown" optional="YES" attributeType="String"/>
|
||||
<attribute name="messageTimestamp" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="portNum" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="read" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="realACK" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="receivedACK" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="replyID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="fromUser" optional="YES" maxCount="1" deletionRule="Nullify" ordered="YES" destinationEntity="UserEntity" inverseName="sentMessages" inverseEntity="UserEntity"/>
|
||||
<relationship name="toUser" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="receivedMessages" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="messageId"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="MQTTConfigEntity" representedClassName="MQTTConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="encryptionEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="jsonEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPositionPrecision" optional="YES" attributeType="Integer 32" defaultValueString="13" usesScalarValueType="YES"/>
|
||||
<attribute name="mapPublishIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="mapReportingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="password" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<attribute name="proxyToClientEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="root" optional="YES" attributeType="String" defaultValueString="msh"/>
|
||||
<attribute name="tlsEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="username" optional="YES" attributeType="String" maxValueString="30"/>
|
||||
<relationship name="mqttConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="mqttConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="MyInfoEntity" representedClassName="MyInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminIndex" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="deviceId" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="minAppVersion" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="myNodeNum" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rebootCount" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="registered" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="channels" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="ChannelEntity" inverseName="myInfoChannel" inverseEntity="ChannelEntity"/>
|
||||
<relationship name="myInfoNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="myInfo" inverseEntity="NodeInfoEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="myNodeNum"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="NetworkConfigEntity" representedClassName="NetworkConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="dns" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabledProtocols" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ethEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="gateway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ip" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ntpServer" optional="YES" attributeType="String"/>
|
||||
<attribute name="subnet" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiMode" optional="YES" attributeType="Integer 32" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiPsk" optional="YES" attributeType="String" minValueString="0" maxValueString="60"/>
|
||||
<attribute name="wifiSsid" optional="YES" attributeType="String" minValueString="0" maxValueString="30"/>
|
||||
<relationship name="networkConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="networkConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="NodeInfoEntity" representedClassName="NodeInfoEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleName" optional="YES" attributeType="String"/>
|
||||
<attribute name="channel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="favorite" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="firstHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="hopsAway" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="ignored" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeard" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="peripheralId" optional="YES" attributeType="String"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="sessionExpiration" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="sessionPasskey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="viaMqtt" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="ambientLightingConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="AmbientLightingConfigEntity" inverseName="ambientLightingConfigNode" inverseEntity="AmbientLightingConfigEntity"/>
|
||||
<relationship name="bluetoothConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="BluetoothConfigEntity" inverseName="bluetoothConfigNode" inverseEntity="BluetoothConfigEntity"/>
|
||||
<relationship name="cannedMessageConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="CannedMessageConfigEntity" inverseName="cannedMessagesConfigNode" inverseEntity="CannedMessageConfigEntity"/>
|
||||
<relationship name="detectionSensorConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DetectionSensorConfigEntity" inverseName="detectionSensorConfigNode" inverseEntity="DetectionSensorConfigEntity"/>
|
||||
<relationship name="deviceConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DeviceConfigEntity" inverseName="deviceConfigNode" inverseEntity="DeviceConfigEntity"/>
|
||||
<relationship name="displayConfig" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="DisplayConfigEntity" inverseName="displayConfigNode" inverseEntity="DisplayConfigEntity"/>
|
||||
<relationship name="externalNotificationConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ExternalNotificationConfigEntity" inverseName="externalNotificationConfigNode" inverseEntity="ExternalNotificationConfigEntity"/>
|
||||
<relationship name="loRaConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="LoRaConfigEntity" inverseName="loRaConfigNode" inverseEntity="LoRaConfigEntity"/>
|
||||
<relationship name="metadata" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="DeviceMetadataEntity" inverseName="metadataNode" inverseEntity="DeviceMetadataEntity"/>
|
||||
<relationship name="mqttConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MQTTConfigEntity" inverseName="mqttConfigNode" inverseEntity="MQTTConfigEntity"/>
|
||||
<relationship name="myInfo" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="MyInfoEntity" inverseName="myInfoNode" inverseEntity="MyInfoEntity"/>
|
||||
<relationship name="networkConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NetworkConfigEntity" inverseName="networkConfigNode" inverseEntity="NetworkConfigEntity"/>
|
||||
<relationship name="pax" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PaxCounterEntity" inverseName="paxNode" inverseEntity="PaxCounterEntity"/>
|
||||
<relationship name="paxCounterConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PaxCounterConfigEntity" inverseName="paxCounterConfigNode" inverseEntity="PaxCounterConfigEntity"/>
|
||||
<relationship name="positionConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PositionConfigEntity" inverseName="positionConfigNode" inverseEntity="PositionConfigEntity"/>
|
||||
<relationship name="positions" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PositionEntity" inverseName="nodePosition" inverseEntity="PositionEntity"/>
|
||||
<relationship name="powerConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PowerConfigEntity" inverseName="powerConfigNode" inverseEntity="PowerConfigEntity"/>
|
||||
<relationship name="rangeTestConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RangeTestConfigEntity" inverseName="rangeTestConfigNode" inverseEntity="RangeTestConfigEntity"/>
|
||||
<relationship name="rtttlConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="RTTTLConfigEntity" inverseName="rtttlConfigNode" inverseEntity="RTTTLConfigEntity"/>
|
||||
<relationship name="securityConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecurityConfigEntity" inverseName="securityConfigNode" inverseEntity="SecurityConfigEntity"/>
|
||||
<relationship name="serialConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SerialConfigEntity" inverseName="serialConfigNode" inverseEntity="SerialConfigEntity"/>
|
||||
<relationship name="storeForwardConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreForwardConfigEntity" inverseName="storeForwardConfigNode" inverseEntity="StoreForwardConfigEntity"/>
|
||||
<relationship name="telemetries" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TelemetryEntity" inverseName="nodeTelemetry" inverseEntity="TelemetryEntity"/>
|
||||
<relationship name="telemetryConfig" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TelemetryConfigEntity" inverseName="telemetryConfigNode" inverseEntity="TelemetryConfigEntity"/>
|
||||
<relationship name="traceRoutes" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="TraceRouteEntity" inverseName="node" inverseEntity="TraceRouteEntity"/>
|
||||
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="UserEntity" inverseName="userNode" inverseEntity="UserEntity"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="num"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="PaxCounterConfigEntity" representedClassName="PaxCounterConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bleThreshold" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="updateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifiThreshold" optional="YES" attributeType="Integer 32" defaultValueString="-80" usesScalarValueType="YES"/>
|
||||
<relationship name="paxCounterConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="paxCounterConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PaxCounterEntity" representedClassName="PaxCounterEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ble" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="wifi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="paxNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="pax" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionConfigEntity" representedClassName="PositionConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="broadcastSmartMinimumDistance" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="broadcastSmartMinimumIntervalSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceGpsEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="fixedPosition" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsAttemptTime" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsEnGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsMode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="gpsUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionBroadcastSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="positionFlags" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="rxGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="smartPositionEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="txGpio" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="positionConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positionConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PositionEntity" representedClassName="PositionEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="heading" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="latest" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="precisionBits" optional="YES" attributeType="Integer 32" defaultValueString="32" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="satsInView" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="seqNo" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="speed" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="nodePosition" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="positions" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="PowerConfigEntity" representedClassName="PowerConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adcMultiplierOverride" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="deviceBatteryInaAddress" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isPowerSaving" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="lsSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="minWakeSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="onBatteryShutdownAfterSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="waitBluetoothSecs" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="powerConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="powerConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RangeTestConfigEntity" representedClassName="RangeTestConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="save" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="sender" optional="YES" attributeType="Integer 32" usesScalarValueType="YES"/>
|
||||
<relationship name="rangeTestConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rangeTestConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="RouteEntity" representedClassName="RouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="color" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="distance" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="elevationGain" optional="YES" attributeType="Double" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="notes" optional="YES" attributeType="String"/>
|
||||
<relationship name="locations" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="LocationEntity" inverseName="routeLocation" inverseEntity="LocationEntity"/>
|
||||
</entity>
|
||||
<entity name="RTTTLConfigEntity" representedClassName="RTTTLConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="ringtone" optional="YES" attributeType="String" maxValueString="228" defaultValueString=""/>
|
||||
<relationship name="rtttlConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="rtttlConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SecurityConfigEntity" representedClassName="SecurityConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="adminChannelEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="adminKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="adminKey2" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="adminKey3" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="bluetoothLoggingEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="debugLogApiEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="isManaged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="privateKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="serialEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<relationship name="securityConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="securityConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="SerialConfigEntity" representedClassName="SerialConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="baudRate" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="echo" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="mode" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="overrideConsoleSerialPort" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="rxd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="timeout" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="txd" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="serialConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="serialConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="StoreForwardConfigEntity" representedClassName="StoreForwardConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="heartbeat" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnMax" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="historyReturnWindow" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isRouter" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="lastHeartbeat" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="lastRequest" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="records" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="storeForwardConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="storeForwardConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryConfigEntity" representedClassName="TelemetryConfigEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="deviceUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentDisplayFahrenheit" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentMeasurementEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="environmentUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="powerMeasurementEnabled" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="powerScreenEnabled" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="powerUpdateInterval" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<relationship name="telemetryConfigNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetryConfig" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TelemetryEntity" representedClassName="TelemetryEntity" syncable="YES">
|
||||
<attribute name="airUtilTx" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="barometricPressure" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="batteryLevel" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="channelUtilization" optional="YES" attributeType="Float" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="current" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="gasResistance" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="iaq" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="irLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="lux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="metricsType" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numOnlineNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsRx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsRxBad" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numPacketsTx" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numRxDupe" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTotalNodes" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTxRelay" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numTxRelayCanceled" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh1Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh1Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh2Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh2Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh3Current" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="powerCh3Voltage" optional="YES" attributeType="Float" usesScalarValueType="YES"/>
|
||||
<attribute name="radiation" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rainfall1H" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rainfall24H" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="relativeHumidity" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="rssi" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="soilMoisture" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="soilTemperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="temperature" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="uptimeSeconds" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="uvLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="voltage" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="weight" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="whiteLux" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windDirection" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="windGust" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windLull" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="windSpeed" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="nodeTelemetry" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="telemetries" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteEntity" representedClassName="TraceRouteEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hasPositions" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="hopsBack" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="hopsTowards" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="response" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="routeBackText" optional="YES" attributeType="String"/>
|
||||
<attribute name="routeText" optional="YES" attributeType="String"/>
|
||||
<attribute name="sent" optional="YES" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="hops" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="TraceRouteHopEntity" inverseName="traceRoute" inverseEntity="TraceRouteHopEntity"/>
|
||||
<relationship name="node" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="traceRoutes" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="TraceRouteHopEntity" representedClassName="TraceRouteHopEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="altitude" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="back" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="latitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="longitudeI" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String"/>
|
||||
<attribute name="num" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="snr" optional="YES" attributeType="Float" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="time" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<relationship name="traceRoute" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TraceRouteEntity" inverseName="hops" inverseEntity="TraceRouteEntity"/>
|
||||
</entity>
|
||||
<entity name="UserEntity" representedClassName="UserEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hwDisplayName" optional="YES" attributeType="String"/>
|
||||
<attribute name="hwModel" attributeType="String"/>
|
||||
<attribute name="hwModelId" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="isLicensed" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="keyMatch" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
||||
<attribute name="lastMessage" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="longName" attributeType="String"/>
|
||||
<attribute name="mute" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="newPublicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="num" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="numString" optional="YES" attributeType="String"/>
|
||||
<attribute name="pkiEncrypted" optional="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="publicKey" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="role" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="shortName" attributeType="String"/>
|
||||
<attribute name="unmessagable" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="userId" attributeType="String"/>
|
||||
<relationship name="receivedMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="toUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="sentMessages" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="MessageEntity" inverseName="fromUser" inverseEntity="MessageEntity"/>
|
||||
<relationship name="userNode" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="NodeInfoEntity" inverseName="user" inverseEntity="NodeInfoEntity"/>
|
||||
</entity>
|
||||
<entity name="WaypointEntity" representedClassName="WaypointEntity" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="created" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="expire" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="icon" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="id" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="lastUpdated" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="latitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="locked" attributeType="Integer 64" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="longDescription" optional="YES" attributeType="String" maxValueString="100"/>
|
||||
<attribute name="longitudeI" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="name" attributeType="String" minValueString="1" maxValueString="30"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="id"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
</model>
|
||||
|
|
@ -4,18 +4,14 @@ import SwiftUI
|
|||
import CoreData
|
||||
import OSLog
|
||||
import TipKit
|
||||
import MeshtasticProtobufs
|
||||
|
||||
@main
|
||||
struct MeshtasticAppleApp: App {
|
||||
|
||||
@UIApplicationDelegateAdaptor(MeshtasticAppDelegate.self)
|
||||
private var appDelegate
|
||||
@UIApplicationDelegateAdaptor(MeshtasticAppDelegate.self) private var appDelegate
|
||||
|
||||
@ObservedObject
|
||||
var appState: AppState
|
||||
|
||||
// @ObservedObject
|
||||
// private var bleManager: BLEManager
|
||||
@ObservedObject var appState: AppState
|
||||
|
||||
private let persistenceController: PersistenceController
|
||||
|
||||
|
|
@ -24,6 +20,7 @@ struct MeshtasticAppleApp: App {
|
|||
@State var incomingUrl: URL?
|
||||
@State var channelSettings: String?
|
||||
@State var addChannels = false
|
||||
public var minimumContactVersion = "2.6.9"
|
||||
|
||||
init() {
|
||||
let persistenceController = PersistenceController.shared
|
||||
|
|
@ -59,8 +56,10 @@ struct MeshtasticAppleApp: App {
|
|||
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
|
||||
Logger.mesh.debug("URL received \(userActivity, privacy: .public)")
|
||||
self.incomingUrl = userActivity.webpageURL
|
||||
|
||||
if (self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/#")) != nil {
|
||||
self.saveChannels = false
|
||||
if self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/v/#") == true {
|
||||
handleContactUrl(url: self.incomingUrl!)
|
||||
} else if self.incomingUrl?.absoluteString.lowercased().contains("meshtastic.org/e/#") == true {
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.addChannels = Bool(self.incomingUrl?["add"] ?? "false") ?? false
|
||||
if (self.incomingUrl?.absoluteString.lowercased().contains("?")) != nil {
|
||||
|
|
@ -84,10 +83,11 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
}
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
Logger.mesh.debug("Some sort of URL was received \(url, privacy: .public)")
|
||||
self.incomingUrl = url
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if url.absoluteString.lowercased().contains("meshtastic.org/v/#") {
|
||||
handleContactUrl(url: url)
|
||||
} else if url.absoluteString.lowercased().contains("meshtastic.org/e/#") {
|
||||
if let components = self.incomingUrl?.absoluteString.components(separatedBy: "#") {
|
||||
self.addChannels = Bool(self.incomingUrl?["add"] ?? "false") ?? false
|
||||
if self.incomingUrl?.absoluteString.lowercased().contains("?") != nil {
|
||||
|
|
@ -119,7 +119,7 @@ struct MeshtasticAppleApp: App {
|
|||
.displayFrequency(.immediate)
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase) { (_, newScenePhase) in
|
||||
switch newScenePhase {
|
||||
|
|
@ -143,4 +143,76 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handleContactUrl(url: URL) {
|
||||
let supportedVersion = UserDefaults.firmwareVersion == "0.0.0" || self.minimumContactVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedAscending || minimumContactVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedSame
|
||||
if !supportedVersion {
|
||||
// Show an alert letting the user know they need to upgrade their firmware to use the contact import.
|
||||
let alertController = UIAlertController(
|
||||
title: "Firmware Upgrade Required",
|
||||
message: "In order to import contacts via a QR code you need firmware version 2.6.9 or greater.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alertController.addAction(UIAlertAction(
|
||||
title: "Close",
|
||||
style: .cancel,
|
||||
handler: nil
|
||||
))
|
||||
// Present the alert
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootViewController = windowScene.windows.first?.rootViewController {
|
||||
rootViewController.present(alertController, animated: true)
|
||||
}
|
||||
Logger.services.debug("User Alerted that a firmware upgrade is required to import contacts.")
|
||||
} else {
|
||||
let components = url.absoluteString.components(separatedBy: "#")
|
||||
// Extract contact information from the URL
|
||||
if let contactData = components.last {
|
||||
let decodedString = contactData.base64urlToBase64()
|
||||
if let decodedData = Data(base64Encoded: decodedString) {
|
||||
do {
|
||||
let contact = try MeshtasticProtobufs.SharedContact(serializedBytes: decodedData)
|
||||
// Show an alert to confirm adding the contact
|
||||
let alertController = UIAlertController(
|
||||
title: "Add Contact",
|
||||
message: "Would you like to add \(contact.user.longName) as a contact?",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alertController.addAction(UIAlertAction(
|
||||
title: "Yes",
|
||||
style: .default,
|
||||
handler: { _ in
|
||||
let success = BLEManager.shared.addContactFromURL(base64UrlString: contactData)
|
||||
Logger.services.debug("Contact added from URL: \(success ? "success" : "failed")")
|
||||
}
|
||||
))
|
||||
alertController.addAction(UIAlertAction(
|
||||
title: "No",
|
||||
style: .cancel,
|
||||
handler: nil
|
||||
))
|
||||
// Present the alert
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootViewController = windowScene.windows.first?.rootViewController {
|
||||
rootViewController.present(alertController, animated: true)
|
||||
}
|
||||
Logger.services.debug("Contact data extracted from URL: \(contactData, privacy: .public)")
|
||||
} catch {
|
||||
Logger.services.error("Failed to parse contact data: \(error.localizedDescription, privacy: .public)")
|
||||
// Show error alert to user
|
||||
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
|
||||
let rootViewController = windowScene.windows.first?.rootViewController {
|
||||
let errorAlert = UIAlertController(
|
||||
title: "Error",
|
||||
message: "Could not process contact information. Invalid format.",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
errorAlert.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
rootViewController.present(errorAlert, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,22 @@ class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificat
|
|||
if let targetValue = userInfo["target"] as? String,
|
||||
let deepLink = userInfo["path"] as? String,
|
||||
let url = URL(string: deepLink) {
|
||||
Logger.services.info("userNotificationCenter didReceiveResponse \(targetValue, privacy: .public) \(deepLink, privacy: .public)")
|
||||
Logger.services.info("userNotificationCenter didReceiveResponse handling deeplink: \(targetValue, privacy: .public) \(deepLink, privacy: .public)")
|
||||
// Handle TraceRoute notifications specially to ensure they navigate correctly
|
||||
if deepLink.contains("meshtastic:///nodes") && deepLink.contains("nodenum=") {
|
||||
// First extract the node number from the URL
|
||||
if let nodeNumString = deepLink.components(separatedBy: "nodenum=").last,
|
||||
let nodeNum = Int64(nodeNumString) {
|
||||
Logger.services.info("Navigation to specific node via notification: \(nodeNum, privacy: .public)")
|
||||
self.router?.navigationState.selectedTab = .nodes
|
||||
// Post a notification to trigger app-wide refresh
|
||||
NotificationCenter.default.post(name: NSNotification.Name("ForceNavigationRefresh"),
|
||||
object: nil,
|
||||
userInfo: ["nodeNum": nodeNum])
|
||||
self.router?.navigationState.nodeListSelectedNodeNum = nodeNum
|
||||
}
|
||||
}
|
||||
// Still call the regular router in all cases
|
||||
router?.route(url: url)
|
||||
} else {
|
||||
Logger.services.error("Failed to handle notification response: \(userInfo, privacy: .public)")
|
||||
|
|
|
|||
|
|
@ -160,7 +160,9 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newNode.channel = Int32(packet.channel)
|
||||
}
|
||||
if let nodeInfoMessage = try? NodeInfo(serializedBytes: packet.decoded.payload) {
|
||||
newNode.hopsAway = Int32(nodeInfoMessage.hopsAway)
|
||||
if nodeInfoMessage.hasHopsAway {
|
||||
newNode.hopsAway = Int32(nodeInfoMessage.hopsAway)
|
||||
}
|
||||
newNode.favorite = nodeInfoMessage.isFavorite
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +183,18 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
newUser.role = Int32(newUserMessage.role.rawValue)
|
||||
newUser.hwModel = String(describing: newUserMessage.hwModel).uppercased()
|
||||
newUser.hwModelId = Int32(newUserMessage.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if newUserMessage.hasIsUnmessagable {
|
||||
newUser.unmessagable = newUserMessage.isUnmessagable
|
||||
} else {
|
||||
let roles = [2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(newUser.role))
|
||||
if containsRole {
|
||||
newUser.unmessagable = true
|
||||
} else {
|
||||
newUser.unmessagable = false
|
||||
}
|
||||
}
|
||||
if !newUserMessage.publicKey.isEmpty {
|
||||
newUser.pkiEncrypted = true
|
||||
newUser.publicKey = newUserMessage.publicKey
|
||||
|
|
@ -277,6 +291,18 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
fetchedNode[0].user!.role = Int32(nodeInfoMessage.user.role.rawValue)
|
||||
fetchedNode[0].user!.hwModel = String(describing: nodeInfoMessage.user.hwModel).uppercased()
|
||||
fetchedNode[0].user!.hwModelId = Int32(nodeInfoMessage.user.hwModel.rawValue)
|
||||
/// For nodes that have the optional isUnmessagable boolean use that, otherwise excluded roles that are unmessagable by default
|
||||
if nodeInfoMessage.user.hasIsUnmessagable {
|
||||
fetchedNode[0].user!.unmessagable = nodeInfoMessage.user.isUnmessagable
|
||||
} else {
|
||||
let roles = [-1, 2, 4, 5, 6, 7, 10, 11]
|
||||
let containsRole = roles.contains(Int(fetchedNode[0].user?.role ?? -1))
|
||||
if containsRole {
|
||||
fetchedNode[0].user?.unmessagable = true
|
||||
} else {
|
||||
fetchedNode[0].user?.unmessagable = false
|
||||
}
|
||||
}
|
||||
if !nodeInfoMessage.user.publicKey.isEmpty {
|
||||
fetchedNode[0].user!.pkiEncrypted = true
|
||||
fetchedNode[0].user!.publicKey = nodeInfoMessage.user.publicKey
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@ struct Connect: View {
|
|||
Text("Long Name: \(node?.user?.longName?.addingVariationSelectors ?? "Unknown".localized)")
|
||||
Text("BLE RSSI: \(connectedPeripheral.rssi)")
|
||||
|
||||
Button(role: .destructive) {
|
||||
if let connectedPeripheral = bleManager.connectedPeripheral,
|
||||
connectedPeripheral.peripheral.state == .connected {
|
||||
bleManager.disconnectPeripheral(reconnect: false)
|
||||
}
|
||||
} label: {
|
||||
Label("Disconnect", systemImage: "antenna.radiowaves.left.and.right.slash")
|
||||
}
|
||||
Button {
|
||||
if !bleManager.sendShutdown(fromUser: node!.user!, toUser: node!.user!, adminIndex: node!.myInfo!.adminIndex) {
|
||||
Logger.mesh.error("Shutdown Failed")
|
||||
|
|
|
|||
|
|
@ -32,47 +32,64 @@ import Foundation
|
|||
import SwiftUI
|
||||
|
||||
struct SignalStrengthIndicator: View {
|
||||
let signalStrength: BLESignalStrength
|
||||
// Accessibility: VoiceOver description
|
||||
private var accessibilityDescription: String {
|
||||
switch signalStrength {
|
||||
case .weak:
|
||||
return "Signal strength weak".localized
|
||||
case .normal:
|
||||
return "Signal strength normal".localized
|
||||
case .strong:
|
||||
return "Signal strength strong".localized
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
ForEach(0..<3) { bar in
|
||||
RoundedRectangle(cornerRadius: 3)
|
||||
.divided(amount: (CGFloat(bar) + 1) / CGFloat(3))
|
||||
.fill(getColor().opacity(bar <= signalStrength.rawValue ? 1 : 0.3))
|
||||
.frame(width: 8, height: 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
let signalStrength: BLESignalStrength
|
||||
|
||||
private func getColor() -> Color {
|
||||
switch signalStrength {
|
||||
case .weak:
|
||||
return Color.red
|
||||
case .normal:
|
||||
return Color.yellow
|
||||
case .strong:
|
||||
return Color.green
|
||||
}
|
||||
}
|
||||
var body: some View {
|
||||
Group {
|
||||
HStack {
|
||||
ForEach(0..<3) { bar in
|
||||
RoundedRectangle(cornerRadius: 3)
|
||||
.divided(amount: (CGFloat(bar) + 1) / CGFloat(3))
|
||||
.fill(getColor().opacity(bar <= signalStrength.rawValue ? 1 : 0.3))
|
||||
.frame(width: 8, height: 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("Signal strength".localized)
|
||||
.accessibilityValue(accessibilityDescription)
|
||||
}
|
||||
|
||||
private func getColor() -> Color {
|
||||
switch signalStrength {
|
||||
case .weak:
|
||||
return Color.red
|
||||
case .normal:
|
||||
return Color.yellow
|
||||
case .strong:
|
||||
return Color.green
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Divided<S: Shape>: Shape {
|
||||
var amount: CGFloat // Should be in range 0...1
|
||||
var shape: S
|
||||
func path(in rect: CGRect) -> Path {
|
||||
shape.path(in: rect.divided(atDistance: amount * rect.height, from: .maxYEdge).slice)
|
||||
}
|
||||
var amount: CGFloat // Should be in range 0...1
|
||||
var shape: S
|
||||
func path(in rect: CGRect) -> Path {
|
||||
shape.path(in: rect.divided(atDistance: amount * rect.height, from: .maxYEdge).slice)
|
||||
}
|
||||
}
|
||||
|
||||
extension Shape {
|
||||
func divided(amount: CGFloat) -> Divided<Self> {
|
||||
return Divided(amount: amount, shape: self)
|
||||
}
|
||||
func divided(amount: CGFloat) -> Divided<Self> {
|
||||
return Divided(amount: amount, shape: self)
|
||||
}
|
||||
}
|
||||
|
||||
enum BLESignalStrength: Int {
|
||||
case weak = 0
|
||||
case normal = 1
|
||||
case strong = 2
|
||||
case weak = 0
|
||||
case normal = 1
|
||||
case strong = 2
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,69 +13,101 @@ struct BatteryCompact: View {
|
|||
var color: Color
|
||||
|
||||
var body: some View {
|
||||
// Group the battery icon and label in a single accessible container
|
||||
HStack(alignment: .center, spacing: 0) {
|
||||
if let batteryLevel {
|
||||
if batteryLevel == 100 {
|
||||
Image(systemName: "battery.100.bolt")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel < 100 && batteryLevel > 74 {
|
||||
Image(systemName: "battery.75")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel < 75 && batteryLevel > 49 {
|
||||
Image(systemName: "battery.50")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel < 50 && batteryLevel > 14 {
|
||||
Image(systemName: "battery.25")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel < 15 && batteryLevel > 0 {
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel == 0 {
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
} else if batteryLevel > 100 {
|
||||
// Check for plugged in state
|
||||
let isPluggedIn = batteryLevel > 100
|
||||
let isCharging = batteryLevel == 100
|
||||
// Battery icon selection based on level
|
||||
if isPluggedIn {
|
||||
Image(systemName: "powerplug")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true) // Hide from VoiceOver since container will handle it
|
||||
} else if isCharging {
|
||||
Image(systemName: "battery.100.bolt")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
} else if batteryLevel > 74 {
|
||||
Image(systemName: "battery.75")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
} else if batteryLevel > 49 {
|
||||
Image(systemName: "battery.50")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
} else if batteryLevel > 14 {
|
||||
Image(systemName: "battery.25")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
} else if batteryLevel > 0 {
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
} else {
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
if let batteryLevel {
|
||||
if batteryLevel > 100 {
|
||||
// Battery text label
|
||||
if isPluggedIn {
|
||||
Text("PWD")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(font)
|
||||
} else if batteryLevel == 100 {
|
||||
.accessibilityHidden(true)
|
||||
} else if isCharging {
|
||||
Text("CHG")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(font)
|
||||
.accessibilityHidden(true)
|
||||
} else {
|
||||
Text(verbatim: "\(batteryLevel.formatted(.number.precision(.fractionLength(0))))%")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(font)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
} else {
|
||||
// Unknown battery state
|
||||
Image(systemName: "battery.0")
|
||||
.font(iconFont)
|
||||
.foregroundColor(color)
|
||||
.symbolRenderingMode(.multicolor)
|
||||
.accessibilityHidden(true)
|
||||
Text(verbatim: "?")
|
||||
.foregroundStyle(.secondary)
|
||||
.font(font)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
// Setup container-level accessibility for VoiceOver
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(NSLocalizedString("Battery Level", comment: "VoiceOver label for battery gauge"))
|
||||
// Set appropriate value based on the battery state using a computed property
|
||||
.accessibilityValue(batteryLevel.map { level in
|
||||
if level > 100 {
|
||||
// Plugged in - same as PWD visual indicator
|
||||
return "Plugged in".localized
|
||||
} else if level == 100 {
|
||||
// Charging - same as CHG visual indicator
|
||||
return "Charging".localized
|
||||
} else {
|
||||
// Normal battery level
|
||||
return String(format: NSLocalizedString("Battery Level %", comment: "VoiceOver value for battery level"), Int(level))
|
||||
}
|
||||
} ?? "Unknown")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,18 +18,20 @@ struct BatteryGauge: View {
|
|||
|
||||
let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0"))
|
||||
let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity
|
||||
let batteryLevel = Double(mostRecent?.batteryLevel ?? 0)
|
||||
// For VoiceOver purposes, detect when device is plugged in (battery > 100%)
|
||||
let isPluggedIn = (mostRecent?.batteryLevel ?? 0) > 100
|
||||
// Use a capped battery level for UI display
|
||||
let batteryLevel = Double(min(100, mostRecent?.batteryLevel ?? 0))
|
||||
|
||||
VStack {
|
||||
if batteryLevel > 100.0 {
|
||||
// Plugged in
|
||||
Image(systemName: "powerplug")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
if isPluggedIn {
|
||||
// Use a completely standalone view for the plugged in state
|
||||
// to avoid any VoiceOver confusion
|
||||
PluggedInIndicator()
|
||||
} else {
|
||||
let gradient = Gradient(colors: [.red, .orange, .green])
|
||||
Gauge(value: batteryLevel, in: minValue...maxValue) {
|
||||
// Accessibility for battery gauge
|
||||
if batteryLevel >= 0.0 && batteryLevel < 10 {
|
||||
Label("Battery Level %", systemImage: "battery.0")
|
||||
} else if batteryLevel >= 10.0 && batteryLevel < 25.00 {
|
||||
|
|
@ -50,6 +52,8 @@ struct BatteryGauge: View {
|
|||
Text(Int(batteryLevel), format: .percent)
|
||||
}
|
||||
}
|
||||
.accessibilityLabel(NSLocalizedString("Battery Level", comment: "VoiceOver label for battery gauge"))
|
||||
.accessibilityValue(String(format: NSLocalizedString("Battery Level %", comment: "VoiceOver value for battery level"), Int(batteryLevel)))
|
||||
.tint(gradient)
|
||||
.gaugeStyle(.accessoryCircular)
|
||||
}
|
||||
|
|
@ -63,6 +67,23 @@ struct BatteryGauge: View {
|
|||
}
|
||||
}
|
||||
|
||||
/// A dedicated view for showing a device is plugged in
|
||||
/// With proper VoiceOver support that matches the visual indication
|
||||
struct PluggedInIndicator: View {
|
||||
var body: some View {
|
||||
// This view is isolated from any battery measurement
|
||||
// to ensure VoiceOver doesn't pick up any percentages
|
||||
Image(systemName: "powerplug")
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.accentColor)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
// Override the accessibility to ensure correct VoiceOver announcement
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("Battery Level".localized)
|
||||
.accessibilityValue("Plugged in".localized)
|
||||
}
|
||||
}
|
||||
|
||||
struct BatteryGauge_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
|
|
|
|||
|
|
@ -21,22 +21,46 @@ struct ConnectedDevice: View {
|
|||
if (phoneOnly && UIDevice.current.userInterfaceIdiom == .phone) || !phoneOnly {
|
||||
if bluetoothOn {
|
||||
if deviceConnected {
|
||||
if mqttUplinkEnabled || mqttDownlinkEnabled {
|
||||
MQTTIcon(connected: mqttProxyConnected, uplink: mqttUplinkEnabled, downlink: mqttDownlinkEnabled, topic: mqttTopic)
|
||||
}
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
Text(name.addingVariationSelectors).font(name.isEmoji() ? .title : .callout).foregroundColor(.gray)
|
||||
// Create an HStack for connected state with proper accessibility
|
||||
HStack {
|
||||
if mqttUplinkEnabled || mqttDownlinkEnabled {
|
||||
MQTTIcon(connected: mqttProxyConnected, uplink: mqttUplinkEnabled, downlink: mqttDownlinkEnabled, topic: mqttTopic)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.circle.fill")
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.green)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.accessibilityHidden(true)
|
||||
Text(name.addingVariationSelectors)
|
||||
.font(name.isEmoji() ? .title : .callout)
|
||||
.foregroundColor(.gray)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("Connected to Bluetooth device".localized + ", " + name.formatNodeNameForVoiceOver())
|
||||
} else {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.imageScale(.medium)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
// Create a container for disconnected state
|
||||
HStack {
|
||||
Image(systemName: "antenna.radiowaves.left.and.right.slash")
|
||||
.imageScale(.medium)
|
||||
.foregroundColor(.red)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("No Bluetooth device connected".localized)
|
||||
}
|
||||
} else {
|
||||
Text("Bluetooth is off").font(.subheadline).foregroundColor(.red)
|
||||
// Create a container for Bluetooth off state
|
||||
HStack {
|
||||
Text("bluetooth.off".localized)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.red)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel("bluetooth.off".localized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,20 @@ struct LoRaSignalStrengthMeter_Previews: PreviewProvider {
|
|||
static var previews: some View {
|
||||
ScrollView {
|
||||
VStack {
|
||||
VStack {
|
||||
// Good
|
||||
LoRaSignalStrengthMeter(snr: -10, rssi: -100, preset: ModemPresets.longFast, compact: true)
|
||||
.padding(.bottom)
|
||||
// Fair
|
||||
LoRaSignalStrengthMeter(snr: -9.5, rssi: -119, preset: ModemPresets.longFast, compact: true)
|
||||
.padding(.bottom)
|
||||
// Bad
|
||||
LoRaSignalStrengthMeter(snr: -12.75, rssi: -139, preset: ModemPresets.longFast, compact: true)
|
||||
.padding(.bottom)
|
||||
// None
|
||||
LoRaSignalStrengthMeter(snr: -26.0, rssi: -128, preset: ModemPresets.longFast, compact: true)
|
||||
.padding(.bottom)
|
||||
}.padding()
|
||||
HStack {
|
||||
// Good
|
||||
LoRaSignalStrengthMeter(snr: -1, rssi: -114, preset: ModemPresets.longFast, compact: false)
|
||||
|
|
@ -85,16 +99,5 @@ struct LoRaSignalStrengthMeter_Previews: PreviewProvider {
|
|||
}
|
||||
.padding(.top)
|
||||
}
|
||||
|
||||
VStack {
|
||||
// Good
|
||||
LoRaSignalStrengthMeter(snr: -10, rssi: -100, preset: ModemPresets.longFast, compact: true)
|
||||
// Fair
|
||||
LoRaSignalStrengthMeter(snr: -9.5, rssi: -119, preset: ModemPresets.longFast, compact: true)
|
||||
// Bad
|
||||
LoRaSignalStrengthMeter(snr: -12.75, rssi: -139, preset: ModemPresets.longFast, compact: true)
|
||||
// None
|
||||
LoRaSignalStrengthMeter(snr: -26.0, rssi: -128, preset: ModemPresets.longFast, compact: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
113
Meshtastic/Views/Helpers/RateLimitedButton.swift
Normal file
113
Meshtastic/Views/Helpers/RateLimitedButton.swift
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// RateLimitCountdownView.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by Jake Bordens on 5/5/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// This class provides a rate limited button.
|
||||
// Provide a key to differentiate which action is rate-limited
|
||||
// This allows you to keep different rate limits for different action
|
||||
// Rate limits are stored in a RateLimitStorage singleton, but do not persist
|
||||
public struct RateLimitedButton<Content: View>: View {
|
||||
typealias Builder = ((percentComplete: Double, secondsRemaining: TimeInterval)?) -> Content
|
||||
|
||||
let key: String
|
||||
|
||||
@StateObject var storage = RateLimitStorage.shared
|
||||
|
||||
let rateLimit: TimeInterval
|
||||
let content: Builder
|
||||
let action: () -> Void
|
||||
|
||||
init(key: String, rateLimit: TimeInterval, action: @escaping () -> Void, @ViewBuilder label: @escaping Builder) {
|
||||
self.key = key
|
||||
self.rateLimit = rateLimit
|
||||
self.content = label
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
let percentRemaining = storage.rateLimitRemainingPercentage(forKey: key)
|
||||
let secondsRemaining = storage.rateLimitSecondsRemaining(forKey: key)
|
||||
if percentRemaining > 0.0 {
|
||||
content((percentRemaining, secondsRemaining))
|
||||
} else {
|
||||
Button {
|
||||
storage.actionOccured(forKey: key, rateLimit: rateLimit)
|
||||
action()
|
||||
} label: {
|
||||
content(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To store the time an action occured (name by a key) and the time limit
|
||||
// Does not persist across app launches
|
||||
class RateLimitStorage: ObservableObject {
|
||||
private struct RateLimiter {
|
||||
var actionOccuredTimestamp: Date
|
||||
var rateLimitSeconds: TimeInterval
|
||||
|
||||
var rateLimitExpires: Date {
|
||||
return actionOccuredTimestamp.addingTimeInterval(rateLimitSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
static var shared: RateLimitStorage = RateLimitStorage() // Singleton instance
|
||||
|
||||
private var rateLimits = [String: RateLimiter]()
|
||||
private var timer: Timer?
|
||||
|
||||
func actionOccured(forKey key: String, rateLimit: TimeInterval) {
|
||||
let now = Date()
|
||||
if let existingRateLimit = rateLimits[key] {
|
||||
if existingRateLimit.rateLimitExpires > now.addingTimeInterval(rateLimit) {
|
||||
// We have an existing rate limit that is larger than the one being requested
|
||||
// Ignore
|
||||
return
|
||||
}
|
||||
}
|
||||
self.objectWillChange.send()
|
||||
rateLimits[key] = RateLimiter(actionOccuredTimestamp: now, rateLimitSeconds: rateLimit)
|
||||
startTimerIfNecessary()
|
||||
}
|
||||
|
||||
func rateLimitRemainingPercentage(forKey: String) -> Double {
|
||||
guard let rateLimit = rateLimits[forKey] else {
|
||||
return 0.0
|
||||
}
|
||||
let percent = (rateLimit.rateLimitExpires.timeIntervalSinceNow) / rateLimit.rateLimitSeconds
|
||||
return min(1.0, max(percent, 0.0))
|
||||
}
|
||||
|
||||
func rateLimitSecondsRemaining(forKey: String) -> TimeInterval {
|
||||
guard let rateLimit = rateLimits[forKey] else {
|
||||
return 0.0
|
||||
}
|
||||
return rateLimit.rateLimitExpires.timeIntervalSinceNow
|
||||
}
|
||||
|
||||
func startTimerIfNecessary() {
|
||||
// Timer exists, don't create one
|
||||
guard timer == nil else { return }
|
||||
|
||||
// Create the timer
|
||||
self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.objectWillChange.send()
|
||||
|
||||
// Determine if we can clean up the dictionary and stop the timer.
|
||||
let maxExpiration = self.rateLimits.values.map { $0.rateLimitExpires }.max() ?? .distantPast
|
||||
if maxExpiration.timeIntervalSinceNow < 0 {
|
||||
// All rateLimits are in the past. Stop and clean up
|
||||
self.timer?.invalidate()
|
||||
self.timer = nil
|
||||
self.rateLimits.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,8 +33,15 @@ struct ChannelMessageList: View {
|
|||
ZStack(alignment: .bottomTrailing) {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
ForEach(channel.allPrivateMessages) { (message: MessageEntity) in
|
||||
ForEach(Array(channel.allPrivateMessages.enumerated()), id: \.element.id) { index, message in
|
||||
// Get the previous message, if it exists
|
||||
let previousMessage = index > 0 ? channel.allPrivateMessages[index - 1] : nil
|
||||
let currentUser: Bool = (Int64(preferredPeripheralNum) == message.fromUser?.num ? true : false)
|
||||
if message.displayTimestamp(aboveMessage: previousMessage) {
|
||||
Text(message.timestamp.formatted(date: .abbreviated, time: .shortened))
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
if message.replyID > 0 {
|
||||
let messageReply = channel.allPrivateMessages.first(where: { $0.messageId == message.replyID })
|
||||
HStack {
|
||||
|
|
@ -44,7 +51,6 @@ struct ChannelMessageList: View {
|
|||
messageToHighlight = messageNum
|
||||
}
|
||||
scrollView.scrollTo(messageNum, anchor: .center)
|
||||
|
||||
// Reset highlight after delay
|
||||
Task {
|
||||
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ struct RequestPositionButton: View {
|
|||
var body: some View {
|
||||
Button(action: action) {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.accessibilityLabel("Position Exchange Requested".localized)
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
.imageScale(.large)
|
||||
.foregroundColor(.accentColor)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ struct TextMessageSize: View {
|
|||
|
||||
var body: some View {
|
||||
ProgressView("\("Bytes".localized): \(totalBytes) / \(maxbytes)", value: Double(totalBytes), total: Double(maxbytes))
|
||||
.accessibilityLabel(NSLocalizedString("Message Size", comment: "VoiceOver label for message size"))
|
||||
.accessibilityValue(String(format: NSLocalizedString("Bytes Used", comment: "VoiceOver value for bytes used"), totalBytes, maxbytes))
|
||||
.frame(width: 130)
|
||||
.padding(5)
|
||||
.font(.subheadline)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ struct UserList: View {
|
|||
@State private var isPkiEncrypted = false
|
||||
@State private var isFavorite = false
|
||||
@State private var isIgnored = false
|
||||
@State private var isUnmessagable = false
|
||||
@State private var isEnvironment = false
|
||||
@State private var distanceFilter = false
|
||||
@State private var maxDistance: Double = 800000
|
||||
|
|
@ -46,8 +47,8 @@ struct UserList: View {
|
|||
NSSortDescriptor(key: "userNode.lastHeard", ascending: false),
|
||||
NSSortDescriptor(key: "longName", ascending: true)],
|
||||
predicate: NSPredicate(
|
||||
format: "userNode.ignored == false && longName != '' AND NOT (userNode.viaMqtt == YES AND userNode.hopsAway > 0)"
|
||||
), animation: .default
|
||||
format: "userNode.ignored == NO AND unmessagable = NO"
|
||||
), animation: .spring
|
||||
)
|
||||
var users: FetchedResults<UserEntity>
|
||||
|
||||
|
|
@ -60,141 +61,139 @@ struct UserList: View {
|
|||
let localeDateFormat = DateFormatter.dateFormat(fromTemplate: "yyMMdd", options: 0, locale: Locale.current)
|
||||
let dateFormatString = (localeDateFormat ?? "MM/dd/YY")
|
||||
VStack {
|
||||
List(selection: $userSelection) {
|
||||
ForEach(users) { (user: UserEntity) in
|
||||
let mostRecent = user.messageList.last
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0
|
||||
let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0
|
||||
if user.num != bleManager.connectedPeripheral?.num ?? 0 {
|
||||
NavigationLink(value: user) {
|
||||
ZStack {
|
||||
Image(systemName: "circle.fill")
|
||||
.opacity(user.unreadMessages > 0 ? 1 : 0)
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.accentColor)
|
||||
.brightness(0.2)
|
||||
}
|
||||
List(users, selection: $userSelection) { (user: UserEntity) in
|
||||
let mostRecent = user.messageList.last
|
||||
let lastMessageTime = Date(timeIntervalSince1970: TimeInterval(Int64((mostRecent?.messageTimestamp ?? 0 ))))
|
||||
let lastMessageDay = Calendar.current.dateComponents([.day], from: lastMessageTime).day ?? 0
|
||||
let currentDay = Calendar.current.dateComponents([.day], from: Date()).day ?? 0
|
||||
if user.num != bleManager.connectedPeripheral?.num ?? 0 {
|
||||
NavigationLink(value: user) {
|
||||
ZStack {
|
||||
Image(systemName: "circle.fill")
|
||||
.opacity(user.unreadMessages > 0 ? 1 : 0)
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.accentColor)
|
||||
.brightness(0.2)
|
||||
}
|
||||
|
||||
CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))))
|
||||
CircleText(text: user.shortName ?? "?", color: Color(UIColor(hex: UInt32(user.num))))
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
if user.pkiEncrypted {
|
||||
if !user.keyMatch {
|
||||
/// Public Key on the User and the Public Key on the Last Message don't match
|
||||
Image(systemName: "key.slash")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
if user.pkiEncrypted {
|
||||
if !user.keyMatch {
|
||||
/// Public Key on the User and the Public Key on the Last Message don't match
|
||||
Image(systemName: "key.slash")
|
||||
.foregroundColor(.red)
|
||||
} else {
|
||||
Image(systemName: "lock.open.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
Text(user.longName ?? "Unknown".localized)
|
||||
.font(.headline)
|
||||
.allowsTightening(true)
|
||||
Spacer()
|
||||
if user.userNode?.favorite ?? false {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: dateFormatString))
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: dateFormatString))
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Image(systemName: "lock.fill")
|
||||
.foregroundColor(.green)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "lock.open.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
Text(user.longName ?? "Unknown".localized)
|
||||
.font(.headline)
|
||||
.allowsTightening(true)
|
||||
Spacer()
|
||||
if user.userNode?.favorite ?? false {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundColor(.yellow)
|
||||
}
|
||||
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
if lastMessageDay == currentDay {
|
||||
Text(lastMessageTime, style: .time )
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay == (currentDay - 1) {
|
||||
Text("Yesterday")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay < (currentDay - 1) && lastMessageDay > (currentDay - 5) {
|
||||
Text(lastMessageTime.formattedDate(format: dateFormatString))
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
} else if lastMessageDay < (currentDay - 1800) {
|
||||
Text(lastMessageTime.formattedDate(format: dateFormatString))
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
|
||||
if node != nil && !(user.userNode?.favorite ?? false) {
|
||||
let success = bleManager.setFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Favorited a node")
|
||||
}
|
||||
} else {
|
||||
let success = bleManager.removeFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Un Favorited a node")
|
||||
}
|
||||
}
|
||||
context.refresh(user, mergeChanges: true)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save Node Favorite Error")
|
||||
}
|
||||
} label: {
|
||||
Label((user.userNode?.favorite ?? false) ? "Un-Favorite" : "Favorite", systemImage: (user.userNode?.favorite ?? false) ? "star.slash.fill" : "star.fill")
|
||||
}
|
||||
Button {
|
||||
user.mute = !user.mute
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save User Mute Error")
|
||||
}
|
||||
} label: {
|
||||
Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash")
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
Button(role: .destructive) {
|
||||
isPresentingDeleteUserMessagesConfirm = true
|
||||
userSelection = user
|
||||
} label: {
|
||||
Label("Delete Messages", systemImage: "trash")
|
||||
if user.messageList.count > 0 {
|
||||
HStack(alignment: .top) {
|
||||
Text("\(mostRecent != nil ? mostRecent!.messagePayload! : " ")")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"This conversation will be deleted.",
|
||||
isPresented: $isPresentingDeleteUserMessagesConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userSelection!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
.frame(height: 62)
|
||||
.contextMenu {
|
||||
Button {
|
||||
|
||||
if node != nil && !(user.userNode?.favorite ?? false) {
|
||||
let success = bleManager.setFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Favorited a node")
|
||||
}
|
||||
} else {
|
||||
let success = bleManager.removeFavoriteNode(node: user.userNode!, connectedNodeNum: Int64(node!.num))
|
||||
if success {
|
||||
user.userNode?.favorite = !(user.userNode?.favorite ?? true)
|
||||
Logger.data.info("Un Favorited a node")
|
||||
}
|
||||
}
|
||||
context.refresh(user, mergeChanges: true)
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save Node Favorite Error")
|
||||
}
|
||||
} label: {
|
||||
Label((user.userNode?.favorite ?? false) ? "Un-Favorite" : "Favorite", systemImage: (user.userNode?.favorite ?? false) ? "star.slash.fill" : "star.fill")
|
||||
}
|
||||
Button {
|
||||
user.mute = !user.mute
|
||||
do {
|
||||
try context.save()
|
||||
} catch {
|
||||
context.rollback()
|
||||
Logger.data.error("Save User Mute Error")
|
||||
}
|
||||
} label: {
|
||||
Label(user.mute ? "Show Alerts" : "Hide Alerts", systemImage: user.mute ? "bell" : "bell.slash")
|
||||
}
|
||||
if user.messageList.count > 0 {
|
||||
Button(role: .destructive) {
|
||||
isPresentingDeleteUserMessagesConfirm = true
|
||||
userSelection = user
|
||||
} label: {
|
||||
Label("Delete Messages", systemImage: "trash")
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"This conversation will be deleted.",
|
||||
isPresented: $isPresentingDeleteUserMessagesConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button(role: .destructive) {
|
||||
deleteUserMessages(user: userSelection!, context: context)
|
||||
context.refresh(node!.user!, mergeChanges: true)
|
||||
} label: {
|
||||
Text("Delete")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.navigationTitle(String.localizedStringWithFormat("Contacts (%@)".localized, String(users.count == 0 ? 0 : users.count)))
|
||||
.navigationTitle(String.localizedStringWithFormat("Contacts (%@)".localized, String(users.count == 0 ? 0 : users.count - 1)))
|
||||
.sheet(isPresented: $editingFilters) {
|
||||
NodeListFilter(filterTitle: "Contact Filters", viaLora: $viaLora, viaMqtt: $viaMqtt, isOnline: $isOnline, isPkiEncrypted: $isPkiEncrypted, isFavorite: $isFavorite, isIgnored: $isIgnored, isEnvironment: $isEnvironment, distanceFilter: $distanceFilter, maximumDistance: $maxDistance, hopsAway: $hopsAway, roleFilter: $roleFilter, deviceRoles: $deviceRoles)
|
||||
}
|
||||
|
|
@ -247,7 +246,7 @@ struct UserList: View {
|
|||
await searchUserList()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.onFirstAppear {
|
||||
Task {
|
||||
await searchUserList()
|
||||
}
|
||||
|
|
@ -304,11 +303,11 @@ struct UserList: View {
|
|||
let loraPredicate = NSPredicate(format: "userNode.viaMqtt == NO")
|
||||
predicates.append(loraPredicate)
|
||||
} else {
|
||||
let mqttPredicate = NSPredicate(format: "userNode.viaMqtt == YES AND userNode.hopsAway == 0")
|
||||
let mqttPredicate = NSPredicate(format: "userNode.viaMqtt == YES")
|
||||
predicates.append(mqttPredicate)
|
||||
}
|
||||
} else {
|
||||
let mqttPredicate = NSPredicate(format: "NOT (userNode.viaMqtt == YES AND userNode.hopsAway > 0)")
|
||||
let mqttPredicate = NSPredicate(format: "NOT (userNode.viaMqtt == YES)")
|
||||
predicates.append(mqttPredicate)
|
||||
}
|
||||
/// Roles
|
||||
|
|
@ -344,6 +343,13 @@ struct UserList: View {
|
|||
let isFavoritePredicate = NSPredicate(format: "userNode.favorite == YES")
|
||||
predicates.append(isFavoritePredicate)
|
||||
}
|
||||
/// Ignored
|
||||
let isIgnoredPredicate = NSPredicate(format: "userNode.ignored == NO")
|
||||
predicates.append(isIgnoredPredicate)
|
||||
/// Unmessagable
|
||||
let isUnmessagablePredicate = NSPredicate(format: "unmessagable == NO")
|
||||
predicates.append(isUnmessagablePredicate)
|
||||
|
||||
/// Distance
|
||||
if distanceFilter {
|
||||
let pointOfInterest = LocationsHandler.currentLocation
|
||||
|
|
@ -362,16 +368,11 @@ struct UserList: View {
|
|||
predicates.append(distancePredicate)
|
||||
}
|
||||
}
|
||||
|
||||
if predicates.count > 0 || !searchText.isEmpty {
|
||||
if !searchText.isEmpty {
|
||||
let filterPredicates = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
users.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: [textSearchPredicate, filterPredicates])
|
||||
} else {
|
||||
users.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
}
|
||||
if !searchText.isEmpty {
|
||||
let filterPredicates = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
users.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: [textSearchPredicate, filterPredicates])
|
||||
} else {
|
||||
users.nsPredicate = nil
|
||||
users.nsPredicate = NSCompoundPredicate(type: .and, subpredicates: predicates)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ struct UserMessageList: View {
|
|||
@EnvironmentObject var appState: AppState
|
||||
@EnvironmentObject var bleManager: BLEManager
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
||||
// Keyboard State
|
||||
@FocusState var messageFieldFocused: Bool
|
||||
// View State Items
|
||||
|
|
@ -24,16 +23,22 @@ struct UserMessageList: View {
|
|||
@State private var showScrollToBottomButton = false
|
||||
@State private var hasReachedBottom = false
|
||||
@State private var gotFirstUnreadMessage: Bool = false
|
||||
|
||||
@State private var messageToHighlight: Int64 = 0
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
ScrollViewReader { scrollView in
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
ForEach( user.messageList ) { (message: MessageEntity) in
|
||||
ForEach( Array(user.messageList.enumerated()), id: \.element.id) { index, message in
|
||||
// Get the previous message, if it exists
|
||||
let previousMessage = index > 0 ? user.messageList[index - 1] : nil
|
||||
if message.displayTimestamp(aboveMessage: previousMessage) {
|
||||
Text(message.timestamp.formatted(date: .abbreviated, time: .shortened))
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
if user.num != bleManager.connectedPeripheral?.num ?? -1 {
|
||||
let currentUser: Bool = (Int64(UserDefaults.preferredPeripheralNum) == message.fromUser?.num ?? -1 ? true : false)
|
||||
|
||||
|
|
@ -46,7 +51,6 @@ struct UserMessageList: View {
|
|||
messageToHighlight = messageNum
|
||||
}
|
||||
scrollView.scrollTo(messageNum, anchor: .center)
|
||||
|
||||
// Reset highlight after delay
|
||||
Task {
|
||||
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ struct IgnoreNodeButton: View {
|
|||
Image(systemName: node.ignored ? "minus.circle.fill" : "minus.circle")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
// Accessibility: Label for VoiceOver
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,25 +9,28 @@ struct TraceRouteButton: View {
|
|||
private var isPresentingTraceRouteSentAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
RateLimitedButton(key: "traceroute", rateLimit: 30.0) {
|
||||
isPresentingTraceRouteSentAlert = bleManager.sendTraceRouteRequest(
|
||||
destNum: node.user?.num ?? 0,
|
||||
wantResponse: true
|
||||
)
|
||||
} label: {
|
||||
Label {
|
||||
Text("Trace Route")
|
||||
} icon: {
|
||||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}.alert(
|
||||
"Trace Route Sent",
|
||||
isPresented: $isPresentingTraceRouteSentAlert
|
||||
) {
|
||||
Button("OK") { }.keyboardShortcut(.defaultAction)
|
||||
} message: {
|
||||
Text("This could take a while. The response will appear in the trace route log for the node it was sent to.")
|
||||
} label: { completion in
|
||||
if let completion, completion.percentComplete > 0.0 {
|
||||
Label {
|
||||
Text("Trace Route (in \(completion.secondsRemaining.formatted(.number.precision(.fractionLength(0))))s)")
|
||||
.foregroundStyle(.secondary)
|
||||
} icon: {
|
||||
Image("progress.ring.dashed", variableValue: completion.percentComplete)
|
||||
.foregroundStyle(.secondary)
|
||||
}.disabled(true)
|
||||
} else {
|
||||
Label {
|
||||
Text("Trace Route")
|
||||
} icon: {
|
||||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.hierarchical)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct MeshMapContent: MapContent {
|
|||
@AppStorage("meshMapShowNodeHistory") private var showNodeHistory = false
|
||||
@AppStorage("meshMapShowRouteLines") private var showRouteLines = false
|
||||
@AppStorage("enableMapConvexHull") private var showConvexHull = false
|
||||
@AppStorage("enableMapShowFavorites") private var showFavorites = false
|
||||
@Binding var showTraffic: Bool
|
||||
@Binding var showPointsOfInterest: Bool
|
||||
@Binding var selectedMapLayer: MapLayer
|
||||
|
|
@ -39,11 +40,12 @@ struct MeshMapContent: MapContent {
|
|||
@MapContentBuilder
|
||||
var positionAnnotations: some MapContent {
|
||||
ForEach(positions, id: \.id) { position in
|
||||
/// Node color from node.num
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
let positionName = position.nodePosition?.user?.longName ?? "?"
|
||||
/// Latest Position Anotations
|
||||
Annotation(positionName, coordinate: position.coordinate) {
|
||||
if !showFavorites || (position.nodePosition?.favorite == true) {
|
||||
/// Node color from node.num
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
let positionName = position.nodePosition?.user?.longName ?? "?"
|
||||
/// Latest Position Anotations
|
||||
Annotation(positionName, coordinate: position.coordinate) {
|
||||
LazyVStack {
|
||||
ZStack {
|
||||
let nodeColor = UIColor(hex: UInt32(position.nodePosition?.num ?? 0))
|
||||
|
|
@ -59,6 +61,13 @@ struct MeshMapContent: MapContent {
|
|||
.onAppear {
|
||||
self.scale = 1
|
||||
}
|
||||
.onChange(of: showFavorites) {
|
||||
|
||||
scale = 0.5 // Reset to initial state
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
|
||||
scale = 1
|
||||
}
|
||||
}
|
||||
.frame(width: 60, height: 60)
|
||||
}
|
||||
if position.nodePosition?.hasDetectionSensorMetrics ?? false {
|
||||
|
|
@ -130,7 +139,7 @@ struct MeshMapContent: MapContent {
|
|||
}
|
||||
}
|
||||
/// Reduced Precision Map Circles
|
||||
if 10...19 ~= position.precisionBits {
|
||||
if 12...15 ~= position.precisionBits {
|
||||
let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
let radius: CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
if radius > 0.0 {
|
||||
|
|
@ -141,6 +150,8 @@ struct MeshMapContent: MapContent {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@MapContentBuilder
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ struct NodeMapContent: MapContent {
|
|||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
/// Reduced Precision Map Circle
|
||||
if position.latest && 10...19 ~= position.precisionBits {
|
||||
if position.latest && 12...15 ~= position.precisionBits {
|
||||
let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
let radius: CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
if radius > 0.0 {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ struct MapSettingsForm: View {
|
|||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var currentDetent = PresentationDetent.medium
|
||||
@AppStorage("meshMapShowNodeHistory") private var nodeHistory = false
|
||||
@AppStorage("meshMapShowRouteLines") private var routeLines = false
|
||||
@AppStorage("meshMapShowRouteLines") private var enableMapRouteLines = false
|
||||
@AppStorage("enableMapConvexHull") private var convexHull = false
|
||||
@AppStorage("enableMapWaypoints") private var waypoints = true
|
||||
@AppStorage("enableMapWaypoints") private var enableMapWaypoints = true
|
||||
@AppStorage("enableMapShowFavorites") private var enableMapShowFavorites = false
|
||||
@Binding var traffic: Bool
|
||||
@Binding var pointsOfInterest: Bool
|
||||
@Binding var mapLayer: MapLayer
|
||||
|
|
@ -29,7 +30,7 @@ struct MapSettingsForm: View {
|
|||
Picker(selection: $mapLayer, label: Text("")) {
|
||||
ForEach(MapLayer.allCases, id: \.self) { layer in
|
||||
if layer != MapLayer.offline {
|
||||
Text(layer.localized)
|
||||
Text(layer.localized.capitalized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,15 +54,25 @@ struct MapSettingsForm: View {
|
|||
.onChange(of: meshMapDistance) { _, newMeshMapDistance in
|
||||
UserDefaults.meshMapDistance = newMeshMapDistance
|
||||
}
|
||||
Toggle(isOn: $waypoints) {
|
||||
Label("Show Waypoints ", systemImage: "signpost.right.and.left")
|
||||
Toggle(isOn: $enableMapWaypoints) {
|
||||
Label {
|
||||
Text("Show Waypoints")
|
||||
} icon: {
|
||||
Image(systemName: "signpost.right.and.left")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.onTapGesture {
|
||||
UserDefaults.enableMapWaypoints = !waypoints
|
||||
.tint(.accentColor)
|
||||
}
|
||||
Toggle(isOn: $enableMapShowFavorites) {
|
||||
Label {
|
||||
Text("Favorites")
|
||||
} icon: {
|
||||
Image(systemName: "star.fill")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
}
|
||||
|
||||
.tint(.accentColor)
|
||||
Toggle(isOn: $nodeHistory) {
|
||||
Label("Node History", systemImage: "building.columns.fill")
|
||||
}
|
||||
|
|
@ -70,15 +81,10 @@ struct MapSettingsForm: View {
|
|||
self.nodeHistory.toggle()
|
||||
UserDefaults.enableMapNodeHistoryPins = self.nodeHistory
|
||||
}
|
||||
Toggle(isOn: $routeLines) {
|
||||
Toggle(isOn: $enableMapRouteLines) {
|
||||
Label("Route Lines", systemImage: "road.lanes")
|
||||
}
|
||||
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.onTapGesture {
|
||||
self.routeLines.toggle()
|
||||
UserDefaults.enableMapRouteLines = self.routeLines
|
||||
}
|
||||
.tint(.accentColor)
|
||||
Toggle(isOn: $convexHull) {
|
||||
Label("Convex Hull", systemImage: "button.angledbottom.horizontal.right")
|
||||
}
|
||||
|
|
@ -96,9 +102,14 @@ struct MapSettingsForm: View {
|
|||
UserDefaults.enableMapTraffic = self.traffic
|
||||
}
|
||||
Toggle(isOn: $pointsOfInterest) {
|
||||
Label("Points of Interest", systemImage: "mappin.and.ellipse")
|
||||
Label {
|
||||
Text("Points of Interest")
|
||||
} icon: {
|
||||
Image(systemName: "mappin.and.ellipse")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.tint(.accentColor)
|
||||
.onTapGesture {
|
||||
self.pointsOfInterest.toggle()
|
||||
UserDefaults.enableMapPointsOfInterest = self.pointsOfInterest
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ struct PositionPopover: View {
|
|||
if lastLocation.distance(from: CLLocation(latitude: LocationsHandler.DefaultLocation.latitude, longitude: LocationsHandler.DefaultLocation.longitude)) > 0.0 {
|
||||
let metersAway = position.coordinate.distance(from: CLLocationCoordinate2D(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude))
|
||||
Label {
|
||||
Text("distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))")
|
||||
Text("Distance".localized + ": \(distanceFormatter.string(fromDistance: Double(metersAway)))")
|
||||
.foregroundColor(.primary)
|
||||
.font(idiom == .phone ? .callout : .body)
|
||||
} icon: {
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@ struct NodeDetail: View {
|
|||
Section("Hardware") {
|
||||
NodeInfoItem(node: node)
|
||||
}
|
||||
Section("Node") {
|
||||
.accessibilityElement(children: .combine)
|
||||
Section("Node") { // Node
|
||||
HStack(alignment: .center) {
|
||||
Spacer()
|
||||
CircleText(
|
||||
|
|
@ -67,6 +68,7 @@ struct NodeDetail: View {
|
|||
.foregroundColor(getRssiColor(rssi: node.rssi))
|
||||
.font(.caption)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
if node.telemetries?.count ?? 0 > 0 {
|
||||
Spacer()
|
||||
|
|
@ -74,6 +76,7 @@ struct NodeDetail: View {
|
|||
}
|
||||
Spacer()
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
.listRowSeparator(.hidden)
|
||||
if let user = node.user {
|
||||
if !user.keyMatch {
|
||||
|
|
@ -86,6 +89,7 @@ struct NodeDetail: View {
|
|||
.foregroundStyle(.secondary)
|
||||
.font(.callout)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
} icon: {
|
||||
Image(systemName: "key.slash.fill")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
|
|
@ -104,6 +108,7 @@ struct NodeDetail: View {
|
|||
Text(String(node.num))
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
|
||||
HStack {
|
||||
Label {
|
||||
|
|
@ -116,6 +121,7 @@ struct NodeDetail: View {
|
|||
Text(node.num.toHex())
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
|
||||
if let metadata = node.metadata {
|
||||
HStack {
|
||||
|
|
@ -129,6 +135,7 @@ struct NodeDetail: View {
|
|||
|
||||
Text(metadata.firmwareVersion ?? "Unknown".localized)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
|
||||
if let role = node.user?.role, let deviceRole = DeviceRoles(rawValue: Int(role)) {
|
||||
|
|
@ -142,6 +149,20 @@ struct NodeDetail: View {
|
|||
Spacer()
|
||||
Text(deviceRole.name)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
if node.user?.unmessagable ?? false {
|
||||
HStack {
|
||||
Label {
|
||||
Text("Messaging")
|
||||
} icon: {
|
||||
Image(systemName: "iphone.slash")
|
||||
.symbolRenderingMode(.multicolor)
|
||||
}
|
||||
Spacer()
|
||||
Text("Unmonitored")
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
|
||||
if let dm = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")).lastObject as? TelemetryEntity, let uptimeSeconds = dm.uptimeSeconds {
|
||||
|
|
@ -161,6 +182,7 @@ struct NodeDetail: View {
|
|||
Text(uptime)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
|
||||
if let firstHeard = node.firstHeard, firstHeard.timeIntervalSince1970 > 0 && firstHeard < Calendar.current.date(byAdding: .year, value: 1, to: Date())! {
|
||||
|
|
@ -179,7 +201,9 @@ struct NodeDetail: View {
|
|||
Text(firstHeard.formatted())
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
}.onTapGesture {
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
.onTapGesture {
|
||||
dateFormatRelative.toggle()
|
||||
}
|
||||
}
|
||||
|
|
@ -203,7 +227,9 @@ struct NodeDetail: View {
|
|||
Text(lastHeard.formatted())
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
}.onTapGesture {
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
.onTapGesture {
|
||||
dateFormatRelative.toggle()
|
||||
}
|
||||
}
|
||||
|
|
@ -216,79 +242,84 @@ struct NodeDetail: View {
|
|||
if node.hasPositions && UserDefaults.environmentEnableWeatherKit
|
||||
|| node.hasDataForLatestEnvironmentMetrics(attributes: ["iaq", "temperature", "relativeHumidity", "barometricPressure", "windSpeed", "radiation", "weight", "Distance", "soilTemperature", "soilMoisture"]) {
|
||||
Section("Environment") {
|
||||
if !node.hasEnvironmentMetrics {
|
||||
LocalWeatherConditions(location: node.latestPosition?.nodeLocation)
|
||||
} else {
|
||||
VStack {
|
||||
if node.latestEnvironmentMetrics?.iaq ?? -1 > 0 {
|
||||
IndoorAirQuality(iaq: Int(node.latestEnvironmentMetrics?.iaq ?? 0), displayMode: .gradient)
|
||||
.padding(.vertical)
|
||||
}
|
||||
LazyVGrid(columns: gridItemLayout) {
|
||||
if let temperature = node.latestEnvironmentMetrics?.temperature?.shortFormattedTemperature() {
|
||||
WeatherConditionsCompactWidget(temperature: String(temperature), symbolName: "cloud.sun", description: "TEMP")
|
||||
// Group weather/environment data for better VoiceOver experience
|
||||
VStack {
|
||||
if !node.hasEnvironmentMetrics {
|
||||
LocalWeatherConditions(location: node.latestPosition?.nodeLocation)
|
||||
} else {
|
||||
VStack {
|
||||
if node.latestEnvironmentMetrics?.iaq ?? -1 > 0 {
|
||||
IndoorAirQuality(iaq: Int(node.latestEnvironmentMetrics?.iaq ?? 0), displayMode: .gradient)
|
||||
.padding(.vertical)
|
||||
}
|
||||
if let humidity = node.latestEnvironmentMetrics?.relativeHumidity {
|
||||
if let temperature = node.latestEnvironmentMetrics?.temperature {
|
||||
let dewPoint = calculateDewPoint(temp: temperature, relativeHumidity: humidity)
|
||||
.formatted(.number.precision(.fractionLength(0))) + "°"
|
||||
HumidityCompactWidget(humidity: Int(humidity), dewPoint: dewPoint)
|
||||
} else {
|
||||
HumidityCompactWidget(humidity: Int(humidity), dewPoint: nil)
|
||||
LazyVGrid(columns: gridItemLayout) {
|
||||
if let temperature = node.latestEnvironmentMetrics?.temperature?.shortFormattedTemperature() {
|
||||
WeatherConditionsCompactWidget(temperature: String(temperature), symbolName: "cloud.sun", description: "TEMP")
|
||||
}
|
||||
if let humidity = node.latestEnvironmentMetrics?.relativeHumidity {
|
||||
if let temperature = node.latestEnvironmentMetrics?.temperature {
|
||||
let dewPoint = calculateDewPoint(temp: temperature, relativeHumidity: humidity)
|
||||
.formatted(.number.precision(.fractionLength(0))) + "°"
|
||||
HumidityCompactWidget(humidity: Int(humidity), dewPoint: dewPoint)
|
||||
} else {
|
||||
HumidityCompactWidget(humidity: Int(humidity), dewPoint: nil)
|
||||
}
|
||||
}
|
||||
if let pressure = node.latestEnvironmentMetrics?.barometricPressure {
|
||||
PressureCompactWidget(pressure: pressure.formatted(.number.precision(.fractionLength(2))), unit: "hPA", low: pressure <= 1009.144)
|
||||
}
|
||||
if let windSpeed = node.latestEnvironmentMetrics?.windSpeed {
|
||||
let windSpeedMeasurement = Measurement(value: Double(windSpeed), unit: UnitSpeed.metersPerSecond)
|
||||
let windGust = node.latestEnvironmentMetrics?.windGust.map { Measurement(value: Double($0), unit: UnitSpeed.metersPerSecond) }
|
||||
let direction = cardinalValue(from: Double(node.latestEnvironmentMetrics?.windDirection ?? 0))
|
||||
WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))),
|
||||
gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction)
|
||||
}
|
||||
if let rainfall1h = node.latestEnvironmentMetrics?.rainfall1H {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
|
||||
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
|
||||
let unitLabel = usesMetricSystem ? "mm" : "in"
|
||||
let measurement = Measurement(value: Double(rainfall1h), unit: UnitLength.millimeters)
|
||||
let decimals = usesMetricSystem ? 0 : 1
|
||||
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
|
||||
RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel)
|
||||
}
|
||||
if let rainfall24h = node.latestEnvironmentMetrics?.rainfall24H {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
|
||||
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
|
||||
let unitLabel = usesMetricSystem ? "mm" : "in"
|
||||
let measurement = Measurement(value: Double(rainfall24h), unit: UnitLength.millimeters)
|
||||
let decimals = usesMetricSystem ? 0 : 1
|
||||
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
|
||||
RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel)
|
||||
}
|
||||
if let radiation = node.latestEnvironmentMetrics?.radiation {
|
||||
RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr")
|
||||
}
|
||||
if let weight = node.latestEnvironmentMetrics?.weight {
|
||||
WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg")
|
||||
}
|
||||
if let distance = node.latestEnvironmentMetrics?.distance {
|
||||
DistanceCompactWidget(distance: distance.formatted(.number.precision(.fractionLength(0))), unit: "mm")
|
||||
}
|
||||
if let soilTemperature = node.latestEnvironmentMetrics?.soilTemperature {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
|
||||
let unit = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? "°F" : "°C"
|
||||
SoilTemperatureCompactWidget(temperature: soilTemperature.localeTemperature().formatted(.number.precision(.fractionLength(0))), unit: unit)
|
||||
}
|
||||
if let soilMoisture = node.latestEnvironmentMetrics?.soilMoisture {
|
||||
SoilMoistureCompactWidget(moisture: soilMoisture.formatted(.number.precision(.fractionLength(0))), unit: "%")
|
||||
}
|
||||
}
|
||||
if let pressure = node.latestEnvironmentMetrics?.barometricPressure {
|
||||
PressureCompactWidget(pressure: pressure.formatted(.number.precision(.fractionLength(2))), unit: "hPA", low: pressure <= 1009.144)
|
||||
}
|
||||
if let windSpeed = node.latestEnvironmentMetrics?.windSpeed {
|
||||
let windSpeedMeasurement = Measurement(value: Double(windSpeed), unit: UnitSpeed.metersPerSecond)
|
||||
let windGust = node.latestEnvironmentMetrics?.windGust.map { Measurement(value: Double($0), unit: UnitSpeed.metersPerSecond) }
|
||||
let direction = cardinalValue(from: Double(node.latestEnvironmentMetrics?.windDirection ?? 0))
|
||||
WindCompactWidget(speed: windSpeedMeasurement.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))),
|
||||
gust: node.latestEnvironmentMetrics?.windGust ?? 0.0 > 0.0 ? windGust?.formatted(.measurement(width: .abbreviated, numberFormatStyle: .number.precision(.fractionLength(0)))) : "", direction: direction)
|
||||
}
|
||||
if let rainfall1h = node.latestEnvironmentMetrics?.rainfall1H {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
|
||||
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
|
||||
let unitLabel = usesMetricSystem ? "mm" : "in"
|
||||
let measurement = Measurement(value: Double(rainfall1h), unit: UnitLength.millimeters)
|
||||
let decimals = usesMetricSystem ? 0 : 1
|
||||
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
|
||||
RainfallCompactWidget(timespan: .rainfall1H, rainfall: formattedRain, unit: unitLabel)
|
||||
}
|
||||
if let rainfall24h = node.latestEnvironmentMetrics?.rainfall24H {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let usesMetricSystem = locale.usesMetricSystem // Returns true for metric (mm), false for imperial (inches)
|
||||
let unit = usesMetricSystem ? UnitLength.millimeters : UnitLength.inches
|
||||
let unitLabel = usesMetricSystem ? "mm" : "in"
|
||||
let measurement = Measurement(value: Double(rainfall24h), unit: UnitLength.millimeters)
|
||||
let decimals = usesMetricSystem ? 0 : 1
|
||||
let formattedRain = measurement.converted(to: unit).value.formatted(.number.precision(.fractionLength(decimals)))
|
||||
RainfallCompactWidget(timespan: .rainfall24H, rainfall: formattedRain, unit: unitLabel)
|
||||
}
|
||||
if let radiation = node.latestEnvironmentMetrics?.radiation {
|
||||
RadiationCompactWidget(radiation: radiation.formatted(.number.precision(.fractionLength(1))), unit: "µR/hr")
|
||||
}
|
||||
if let weight = node.latestEnvironmentMetrics?.weight {
|
||||
WeightCompactWidget(weight: weight.formatted(.number.precision(.fractionLength(1))), unit: "kg")
|
||||
}
|
||||
if let distance = node.latestEnvironmentMetrics?.distance {
|
||||
DistanceCompactWidget(distance: distance.formatted(.number.precision(.fractionLength(0))), unit: "mm")
|
||||
}
|
||||
if let soilTemperature = node.latestEnvironmentMetrics?.soilTemperature {
|
||||
let locale = NSLocale.current as NSLocale
|
||||
let localeUnit = locale.object(forKey: NSLocale.Key(rawValue: "kCFLocaleTemperatureUnitKey"))
|
||||
let unit = localeUnit as? String ?? "Celsius" == "Fahrenheit" ? "°F" : "°C"
|
||||
SoilTemperatureCompactWidget(temperature: soilTemperature.localeTemperature().formatted(.number.precision(.fractionLength(0))), unit: unit)
|
||||
}
|
||||
if let soilMoisture = node.latestEnvironmentMetrics?.soilMoisture {
|
||||
SoilMoistureCompactWidget(moisture: soilMoisture.formatted(.number.precision(.fractionLength(0))), unit: "%")
|
||||
}
|
||||
.padding(node.latestEnvironmentMetrics?.iaq ?? -1 > 0 ? .bottom : .vertical)
|
||||
}
|
||||
.padding(node.latestEnvironmentMetrics?.iaq ?? -1 > 0 ? .bottom : .vertical)
|
||||
}
|
||||
}
|
||||
// Apply accessibility properties to the environment section
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
if node.hasPowerMetrics && node.latestPowerMetrics != nil {
|
||||
|
|
@ -298,6 +329,7 @@ struct NodeDetail: View {
|
|||
PowerMetrics(metric: metric)
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
Section("Logs") {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ struct NodeInfoItem: View {
|
|||
.foregroundStyle(.gray)
|
||||
.font(.callout)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
Spacer()
|
||||
}
|
||||
VStack(alignment: .center) {
|
||||
|
|
@ -49,9 +50,11 @@ struct NodeInfoItem: View {
|
|||
.cornerRadius(5)
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
.onAppear {
|
||||
Api().loadDeviceHardwareData { (hw) in
|
||||
for device in hw {
|
||||
|
|
@ -79,6 +82,7 @@ struct NodeInfoItem: View {
|
|||
Text(String("incomplete".localized))
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,96 @@
|
|||
|
||||
import SwiftUI
|
||||
import CoreLocation
|
||||
import Foundation
|
||||
|
||||
struct NodeListItem: View {
|
||||
|
||||
// Accessibility: Synthesized description for VoiceOver
|
||||
private var accessibilityDescription: String {
|
||||
var desc = ""
|
||||
if let shortName = node.user?.shortName {
|
||||
// Format the shortName using the String extension method
|
||||
desc = shortName.formatNodeNameForVoiceOver()
|
||||
} else if let longName = node.user?.longName {
|
||||
desc = longName
|
||||
} else {
|
||||
desc = "Unknown".localized + " " + "Node".localized
|
||||
}
|
||||
if connected {
|
||||
desc += ", currently connected"
|
||||
}
|
||||
if node.favorite {
|
||||
desc += ", favorite"
|
||||
}
|
||||
if node.lastHeard != nil {
|
||||
let formatter = RelativeDateTimeFormatter()
|
||||
formatter.unitsStyle = .full
|
||||
let relative = formatter.localizedString(for: node.lastHeard!, relativeTo: Date())
|
||||
desc += ", last heard " + relative
|
||||
}
|
||||
if node.isOnline {
|
||||
desc += ", online"
|
||||
} else {
|
||||
desc += ", offline"
|
||||
}
|
||||
let role = DeviceRoles(rawValue: Int(node.user?.role ?? 0))
|
||||
if let roleName = role?.name {
|
||||
desc += ", role: \(roleName)"
|
||||
}
|
||||
if node.hopsAway > 0 {
|
||||
desc += ", \(node.hopsAway) hops away"
|
||||
}
|
||||
if let battery = node.latestDeviceMetrics?.batteryLevel {
|
||||
// Check for plugged in and charging states, same logic as in BatteryCompact and BatteryGauge
|
||||
if battery > 100 {
|
||||
desc += ", " + "Plugged in".localized
|
||||
} else if battery == 100 {
|
||||
desc += ", " + "Charging".localized
|
||||
} else {
|
||||
desc += ", battery \(battery)%"
|
||||
}
|
||||
}
|
||||
// Add distance and heading/bearing if available, but only for non-connected nodes
|
||||
if !connected, let (lastPosition, myCoord) = locationData {
|
||||
let nodeCoord = CLLocation(latitude: lastPosition.nodeCoordinate!.latitude, longitude: lastPosition.nodeCoordinate!.longitude)
|
||||
let metersAway = nodeCoord.distance(from: myCoord)
|
||||
// Distance information
|
||||
let distanceFormatter = LengthFormatter()
|
||||
distanceFormatter.unitStyle = .medium
|
||||
let formattedDistance = distanceFormatter.string(fromMeters: metersAway)
|
||||
// For VoiceOver, prepend 'Distance' (localized)
|
||||
desc += ", " + String(format: "%@: %@", "Distance".localized, formattedDistance)
|
||||
// Add bearing/heading information for VoiceOver
|
||||
let trueBearing = getBearingBetweenTwoPoints(point1: myCoord, point2: nodeCoord)
|
||||
let heading = Measurement(value: trueBearing, unit: UnitAngle.degrees)
|
||||
let formattedHeading = heading.formatted(.measurement(width: .narrow, numberFormatStyle: .number.precision(.fractionLength(0))))
|
||||
// Using a direct format without requiring a new localization key
|
||||
desc += ", " + "Heading".localized + " " + formattedHeading
|
||||
}
|
||||
// Add signal strength if available
|
||||
if node.snr != 0 && !node.viaMqtt {
|
||||
let signalStrength: BLESignalStrength
|
||||
if node.snr < -10 {
|
||||
signalStrength = .weak
|
||||
} else if node.snr < 5 {
|
||||
signalStrength = .normal
|
||||
} else {
|
||||
signalStrength = .strong
|
||||
}
|
||||
let signalString: String
|
||||
switch signalStrength {
|
||||
case .weak:
|
||||
signalString = "Signal strength weak".localized
|
||||
case .normal:
|
||||
signalString = "Signal strength normal".localized
|
||||
case .strong:
|
||||
signalString = "Signal strength strong".localized
|
||||
}
|
||||
desc += ", " + signalString
|
||||
}
|
||||
return desc
|
||||
}
|
||||
|
||||
@ObservedObject var node: NodeInfoEntity
|
||||
var connected: Bool
|
||||
var connectedNode: Int64
|
||||
|
|
@ -85,6 +172,11 @@ struct NodeListItem: View {
|
|||
let role = DeviceRoles(rawValue: Int(node.user?.role ?? 0))
|
||||
IconAndText(systemName: role?.systemName ?? "figure",
|
||||
text: "Role: \(role?.name ?? "Unknown".localized)")
|
||||
if node.user?.unmessagable ?? false {
|
||||
IconAndText(systemName: "iphone.slash",
|
||||
renderingMode: .multicolor,
|
||||
text: "Unmonitored")
|
||||
}
|
||||
if node.isStoreForwardRouter {
|
||||
IconAndText(systemName: "envelope.arrow.triangle.branch",
|
||||
renderingMode: .multicolor,
|
||||
|
|
@ -167,7 +259,10 @@ struct NodeListItem: View {
|
|||
}
|
||||
.padding(.top, 4)
|
||||
.padding(.bottom, 4)
|
||||
}
|
||||
// Accessibility: Make the whole row a single element for VoiceOver
|
||||
.accessibilityElement(children: .ignore)
|
||||
.accessibilityLabel(accessibilityDescription)
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultIcon: View {
|
||||
|
|
|
|||
92
Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift
Normal file
92
Meshtastic/Views/Nodes/Helpers/ShareContactQRDialog.swift
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// ShareContactQRDialog.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Created by GitHub Copilot on 5/13/25.
|
||||
|
||||
import SwiftUI
|
||||
import CoreImage.CIFilterBuiltins
|
||||
#if canImport(UIKit)
|
||||
import UIKit
|
||||
#endif
|
||||
import CoreData
|
||||
import MeshtasticProtobufs
|
||||
import OSLog
|
||||
|
||||
struct ShareContactQRDialog: View {
|
||||
let node: NodeInfo
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
var qrString: String {
|
||||
var contact = SharedContact()
|
||||
contact.nodeNum = node.num
|
||||
contact.user = node.user
|
||||
do {
|
||||
let contactString = try contact.serializedData().base64EncodedString()
|
||||
return ("https://meshtastic.org/v/#" + contactString.base64ToBase64url())
|
||||
} catch {
|
||||
Logger.services.error("Error serializing contact: \(error)")
|
||||
return ""
|
||||
}
|
||||
}
|
||||
var qrImage: UIImage {
|
||||
let context = CIContext()
|
||||
let filter = CIFilter.qrCodeGenerator()
|
||||
filter.setValue(Data(qrString.utf8), forKey: "inputMessage")
|
||||
let transform = CGAffineTransform(scaleX: 10, y: 10)
|
||||
if let outputImage = filter.outputImage?.transformed(by: transform),
|
||||
let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
|
||||
return UIImage(cgImage: cgimg)
|
||||
}
|
||||
return UIImage(systemName: "xmark.circle") ?? UIImage()
|
||||
}
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
Text("Share Contact QR")
|
||||
.font(.title2)
|
||||
.padding(.top)
|
||||
Text(node.user.longName)
|
||||
.font(.headline)
|
||||
Image(uiImage: qrImage)
|
||||
.interpolation(.none)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.background(Color(.systemBackground))
|
||||
.cornerRadius(16)
|
||||
.shadow(radius: 4)
|
||||
Text("Scan this QR code to add \(node.user.longName) to another device.")
|
||||
.font(.subheadline)
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.secondary)
|
||||
ShareLink("Share QR Code & Link",
|
||||
item: Image(uiImage: qrImage),
|
||||
subject: Text("Add Meshtastic Node \(node.user.shortName) as a contact"),
|
||||
message: Text(qrString),
|
||||
preview: SharePreview("Add Meshtastic Node \(node.user.shortName) as a contact",
|
||||
image: Image(uiImage: qrImage))
|
||||
)
|
||||
Button("Done") { dismiss() }
|
||||
.buttonStyle(.borderedProminent)
|
||||
.padding(.bottom)
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: 350)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct ShareContactQRDialog_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
var node = NodeInfo()
|
||||
node.num = 123456
|
||||
var userProto = User()
|
||||
userProto.id = "!1234"
|
||||
userProto.longName = "Bud"
|
||||
userProto.shortName = "Bud"
|
||||
userProto.hwModel = HardwareModel.tbeam
|
||||
userProto.role = Config.DeviceConfig.Role.client
|
||||
userProto.publicKey = Data()
|
||||
node.user = userProto
|
||||
|
||||
return ShareContactQRDialog(node: node)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -28,6 +28,8 @@ struct NodeList: View {
|
|||
@State private var isFavorite = false
|
||||
@State private var isIgnored = false
|
||||
@State private var isEnvironment = false
|
||||
// Force refresh ID to make SwiftUI rebuild the view hierarchy
|
||||
@State private var forceRefreshID = UUID()
|
||||
@State private var distanceFilter = false
|
||||
@State private var maxDistance: Double = 800000
|
||||
@State private var hopsAway: Double = -1.0
|
||||
|
|
@ -38,6 +40,8 @@ struct NodeList: View {
|
|||
@State private var isPresentingPositionFailedAlert = false
|
||||
@State private var isPresentingDeleteNodeAlert = false
|
||||
@State private var deleteNodeId: Int64 = 0
|
||||
@State private var isPresentingShareContactQR = false
|
||||
@State private var shareContactNode: NodeInfoEntity?
|
||||
|
||||
var boolFilters: [Bool] {[
|
||||
isFavorite,
|
||||
|
|
@ -76,13 +80,21 @@ struct NodeList: View {
|
|||
/// Allow users to mute notifications for a node even if they are not connected
|
||||
if let user = node.user {
|
||||
NodeAlertsButton(context: context, node: node, user: user)
|
||||
if !user.unmessagable {
|
||||
Button(action: {
|
||||
shareContactNode = node
|
||||
isPresentingShareContactQR = true
|
||||
}) {
|
||||
Label("Share Contact QR", systemImage: "qrcode")
|
||||
}
|
||||
}
|
||||
}
|
||||
if let connectedNode {
|
||||
/// Favoriting a node requires being connected
|
||||
FavoriteNodeButton(bleManager: bleManager, context: context, node: node)
|
||||
/// Don't show message, trace route, position exchange or delete context menu items for the connected node
|
||||
if connectedNode.num != node.num {
|
||||
if !node.viaMqtt || node.viaMqtt && node.hopsAway == 0 {
|
||||
if !(node.user?.unmessagable ?? true) {
|
||||
Button(action: {
|
||||
if let url = URL(string: "meshtastic:///messages?userNum=\(node.num)") {
|
||||
UIApplication.shared.open(url)
|
||||
|
|
@ -91,21 +103,10 @@ struct NodeList: View {
|
|||
Label("Message", systemImage: "message")
|
||||
}
|
||||
}
|
||||
Button {
|
||||
let traceRouteSent = bleManager.sendTraceRouteRequest(
|
||||
destNum: node.num,
|
||||
wantResponse: true
|
||||
)
|
||||
if traceRouteSent {
|
||||
isPresentingTraceRouteSentAlert = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
||||
isPresentingTraceRouteSentAlert = false
|
||||
}
|
||||
}
|
||||
|
||||
} label: {
|
||||
Label("Trace Route", systemImage: "signpost.right.and.left")
|
||||
}
|
||||
TraceRouteButton(
|
||||
bleManager: bleManager,
|
||||
node: node
|
||||
)
|
||||
Button {
|
||||
let positionSent = bleManager.sendPosition(
|
||||
channel: node.channel,
|
||||
|
|
@ -142,6 +143,7 @@ struct NodeList: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
// Use forceRefreshID to completely rebuild the view when notifications update the selected node
|
||||
NavigationSplitView(columnVisibility: $columnVisibility) {
|
||||
List(nodes, id: \.self, selection: $selectedNode) { node in
|
||||
NodeListItem(
|
||||
|
|
@ -231,6 +233,13 @@ struct NodeList: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $isPresentingShareContactQR) {
|
||||
if let node = shareContactNode {
|
||||
ShareContactQRDialog(node: node.toProto())
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.navigationSplitViewColumnWidth(min: 100, ideal: 250, max: 500)
|
||||
.navigationBarItems(
|
||||
|
|
@ -243,6 +252,8 @@ struct NodeList: View {
|
|||
phoneOnly: true
|
||||
)
|
||||
}
|
||||
// Make sure the ZStack passes through accessibility to the ConnectedDevice component
|
||||
.accessibilityElement(children: .contain)
|
||||
)
|
||||
} content: {
|
||||
if let node = selectedNode {
|
||||
|
|
@ -261,6 +272,7 @@ struct NodeList: View {
|
|||
} label: {
|
||||
Image(systemName: "rectangle")
|
||||
}
|
||||
.accessibilityLabel("Hide sidebar")
|
||||
}
|
||||
ConnectedDevice(
|
||||
bluetoothOn: bleManager.isSwitchedOn,
|
||||
|
|
@ -269,6 +281,8 @@ struct NodeList: View {
|
|||
phoneOnly: true
|
||||
)
|
||||
}
|
||||
// Make sure the ZStack passes through accessibility to the ConnectedDevice component
|
||||
.accessibilityElement(children: .contain)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -326,16 +340,40 @@ struct NodeList: View {
|
|||
}
|
||||
.onChange(of: router.navigationState) {
|
||||
if let selected = router.navigationState.nodeListSelectedNodeNum {
|
||||
self.selectedNode = getNodeInfo(id: selected, context: context)
|
||||
// Force a complete view rebuild by generating a new UUID
|
||||
Logger.services.info("Forcing view rebuild with new ID: \(self.forceRefreshID)")
|
||||
// First clear selection
|
||||
self.forceRefreshID = UUID()
|
||||
self.selectedNode = nil
|
||||
// Then after a short delay, set the new selection. Makes it obvious to use page is refreshing too.
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
// Generate another UUID to ensure view gets rebuilt
|
||||
self.forceRefreshID = UUID()
|
||||
self.selectedNode = getNodeInfo(id: selected, context: context)
|
||||
Logger.services.info("Complete view refresh with node: \(selected, privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
self.selectedNode = nil
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
// Set up notification observer for forced refreshes from notifications
|
||||
NotificationCenter.default.addObserver(forName: NSNotification.Name("ForceNavigationRefresh"), object: nil, queue: .main) { notification in
|
||||
if let nodeNum = notification.userInfo?["nodeNum"] as? Int64 {
|
||||
// Force complete refresh of view
|
||||
self.forceRefreshID = UUID()
|
||||
self.selectedNode = getNodeInfo(id: nodeNum, context: self.context)
|
||||
Logger.services.info("NodeList directly updated from notification for node: \(nodeNum, privacy: .public)")
|
||||
}
|
||||
}
|
||||
Task {
|
||||
await searchNodeList()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
// Remove observer when view disappears
|
||||
NotificationCenter.default.removeObserver(self, name: NSNotification.Name("ForceNavigationRefresh"), object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func searchNodeList() async {
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ struct ChannelForm: View {
|
|||
.listRowSeparator(.visible)
|
||||
.onChange(of: preciseLocation) { _, pl in
|
||||
if pl == false {
|
||||
positionPrecision = 14
|
||||
positionPrecision = 15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -157,11 +157,11 @@ struct ChannelForm: View {
|
|||
VStack(alignment: .leading) {
|
||||
Label("Approximate Location", systemImage: "location.slash.circle.fill")
|
||||
|
||||
Slider(value: $positionPrecision, in: 11...14, step: 1) {
|
||||
Slider(value: $positionPrecision, in: 12...15, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "plus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
}
|
||||
Text(PositionPrecision(rawValue: Int(positionPrecision))?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -228,7 +228,7 @@ struct ChannelForm: View {
|
|||
.onChange(of: positionsEnabled) { _, pe in
|
||||
if pe {
|
||||
if positionPrecision == 0 {
|
||||
positionPrecision = 14
|
||||
positionPrecision = 15
|
||||
}
|
||||
} else {
|
||||
positionPrecision = 0
|
||||
|
|
|
|||
|
|
@ -115,8 +115,7 @@ struct BluetoothConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty bluetooth config")
|
||||
_ = bleManager.requestBluetoothConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -273,8 +273,7 @@ struct DeviceConfig: View {
|
|||
} else {
|
||||
if node.deviceConfig == nil {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty device config")
|
||||
_ = bleManager.requestDeviceConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,8 +178,7 @@ struct DisplayConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty display config")
|
||||
_ = bleManager.requestDisplayConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,12 +249,13 @@ struct LoRaConfig: View {
|
|||
let expiration = node.sessionExpiration ?? Date()
|
||||
if expiration < Date() || node.loRaConfig == nil {
|
||||
Logger.mesh.info("⚙️ Empty or expired lora config requesting via PKI admin")
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
if connectedNode.user != nil && node.user != nil {
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty lora config")
|
||||
_ = bleManager.requestLoRaConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,8 +100,7 @@ struct AmbientLightingConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty ambient lighting module config")
|
||||
_ = bleManager.requestAmbientLightingConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,8 +248,7 @@ struct CannedMessagesConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty canned messages module config")
|
||||
_ = bleManager.requestCannedMessagesModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,8 +206,7 @@ struct DetectionSensorConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty detection sensor module config")
|
||||
_ = bleManager.requestDetectionSensorModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,8 +214,7 @@ struct ExternalNotificationConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty external notificaiton module config")
|
||||
_ = bleManager.requestExternalNotificationModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ struct MQTTConfig: View {
|
|||
Label("I have read and understand the above. I voluntarily consent to the unencrypted transmission of my node data via MQTT.", systemImage: "hand.raised")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
}
|
||||
|
|
@ -130,11 +129,11 @@ struct MQTTConfig: View {
|
|||
Text("To comply with privacy laws like CCPA and GDPR, we avoid sharing exact location data. Instead, we use anonymized or approximate (imprecise) location information to protect your privacy.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
Slider(value: $mapPositionPrecision, in: 11...14, step: 1) {
|
||||
Slider(value: $mapPositionPrecision, in: 12...15, step: 1) {
|
||||
} minimumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "plus")
|
||||
} maximumValueLabel: {
|
||||
Image(systemName: "minus")
|
||||
}
|
||||
Text(PositionPrecision(rawValue: Int(mapPositionPrecision))?.description ?? "")
|
||||
.foregroundColor(.gray)
|
||||
|
|
@ -365,8 +364,7 @@ struct MQTTConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty mqtt module config")
|
||||
_ = bleManager.requestMqttModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -430,8 +428,13 @@ struct MQTTConfig: View {
|
|||
self.tlsEnabled = node?.mqttConfig?.tlsEnabled ?? false
|
||||
self.mqttConnected = bleManager.mqttProxyConnected
|
||||
self.mapReportingEnabled = node?.mqttConfig?.mapReportingEnabled ?? false
|
||||
self.mapPublishIntervalSecs = Int(node?.mqttConfig?.mapPublishIntervalSecs ?? 3600)
|
||||
if node?.mqttConfig?.mapPublishIntervalSecs ?? 0 < 3600 {
|
||||
self.mapPublishIntervalSecs = 3600
|
||||
} else {
|
||||
self.mapPublishIntervalSecs = Int(node?.mqttConfig?.mapPublishIntervalSecs ?? 3600)
|
||||
}
|
||||
self.mapPositionPrecision = Double(node?.mqttConfig?.mapPositionPrecision ?? 14)
|
||||
self.mapReportingOptIn = UserDefaults.mapReportingOptIn
|
||||
if mapPositionPrecision < 11 || mapPositionPrecision > 14 {
|
||||
self.mapPositionPrecision = 14
|
||||
self.hasChanges = true
|
||||
|
|
|
|||
|
|
@ -73,8 +73,7 @@ struct PaxCounterConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty pax counter module config")
|
||||
_ = bleManager.requestPaxCounterModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,8 +96,7 @@ struct RangeTestConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty range test module config")
|
||||
_ = bleManager.requestRangeTestModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,8 +87,7 @@ struct RtttlConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty ringtone module config")
|
||||
_ = bleManager.requestRtttlConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,8 +151,7 @@ struct SerialConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty serial module config")
|
||||
_ = bleManager.requestSerialModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,8 +152,7 @@ struct StoreForwardConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty store & forward module config")
|
||||
_ = bleManager.requestStoreAndForwardModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,8 +149,7 @@ struct TelemetryConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty telemetry module config")
|
||||
_ = bleManager.requestTelemetryModuleConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,8 +158,7 @@ struct NetworkConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty network config")
|
||||
_ = bleManager.requestNetworkConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,8 +416,7 @@ struct PositionConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty position config")
|
||||
_ = bleManager.requestPositionConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,8 +143,7 @@ struct PowerConfig: View {
|
|||
}
|
||||
} else {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty power config")
|
||||
_ = bleManager.requestPowerConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,8 +204,7 @@ struct SecurityConfig: View {
|
|||
} else {
|
||||
if node.deviceConfig == nil {
|
||||
/// Legacy Administration
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin, empty security config")
|
||||
_ = bleManager.requestSecurityConfig(fromUser: connectedNode.user!, toUser: node.user!, adminIndex: connectedNode.myInfo?.adminIndex ?? 0)
|
||||
Logger.mesh.info("☠️ Using insecure legacy admin that is no longer supported, please upgrade your firmware.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,13 +25,14 @@ struct UserConfig: View {
|
|||
@State var hasChanges = false
|
||||
@State var shortName = ""
|
||||
@State var longName: String = ""
|
||||
@State var isUnmessagable: Bool = false
|
||||
@State var isLicensed = false
|
||||
@State var overrideDutyCycle = false
|
||||
@State var overrideFrequency: Float = 0.0
|
||||
@State var txPower = 0
|
||||
|
||||
@FocusState var focusedField: Field?
|
||||
|
||||
public var minimumVersion = "2.6.9"
|
||||
let floatFormatter: NumberFormatter = {
|
||||
let formatter = NumberFormatter()
|
||||
formatter.numberStyle = .decimal
|
||||
|
|
@ -96,6 +97,14 @@ struct UserConfig: View {
|
|||
Text("The last 4 of the device MAC address will be appended to the short name to set the device's BLE Name. Short name can be up to 4 bytes long.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.callout)
|
||||
let supportedVersion = UserDefaults.firmwareVersion == "0.0.0" || self.minimumVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedAscending || minimumVersion.compare(UserDefaults.firmwareVersion, options: .numeric) == .orderedSame
|
||||
Toggle(isOn: $isUnmessagable) {
|
||||
Label("Unmessagable", systemImage: "iphone.slash")
|
||||
Text("Used to identify unmonitored or infrastructure nodes so that messaging is not avaliable to nodes that will never respond.")
|
||||
.font(.caption2)
|
||||
}
|
||||
.toggleStyle(SwitchToggleStyle(tint: .accentColor))
|
||||
.disabled(!supportedVersion)
|
||||
}
|
||||
// Only manage ham mode for the locally connected node
|
||||
if node?.num ?? 0 > 0 && node?.num ?? 0 == bleManager.connectedPeripheral?.num ?? 0 {
|
||||
|
|
@ -166,6 +175,7 @@ struct UserConfig: View {
|
|||
var u = User()
|
||||
u.shortName = shortName
|
||||
u.longName = longName
|
||||
u.isUnmessagable = isUnmessagable
|
||||
let adminMessageId = bleManager.saveUser(config: u, fromUser: connectedUser, toUser: node!.user!, adminIndex: connectedNode?.myInfo?.adminIndex ?? 0)
|
||||
if adminMessageId > 0 {
|
||||
hasChanges = false
|
||||
|
|
@ -174,6 +184,7 @@ struct UserConfig: View {
|
|||
} else {
|
||||
var ham = HamParameters()
|
||||
ham.shortName = shortName
|
||||
// ham.isUnmessagable = isUnmessagable
|
||||
ham.callSign = longName
|
||||
ham.txPower = Int32(txPower)
|
||||
ham.frequency = overrideFrequency
|
||||
|
|
@ -199,6 +210,7 @@ struct UserConfig: View {
|
|||
.onAppear {
|
||||
self.shortName = node?.user?.shortName ?? ""
|
||||
self.longName = node?.user?.longName ?? ""
|
||||
self.isUnmessagable = node?.user?.unmessagable ?? false
|
||||
self.isLicensed = node?.user?.isLicensed ?? false
|
||||
self.txPower = Int(node?.loRaConfig?.txPower ?? 0)
|
||||
self.overrideFrequency = node?.loRaConfig?.overrideFrequency ?? 0.00
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/admin.proto
|
||||
|
|
@ -24,7 +25,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
/// This message is handled by the Admin module and is responsible for all settings/channel read/write operations.
|
||||
/// This message is used to do settings operations to both remote AND local nodes.
|
||||
/// (Prior to 1.2 these operations were done via special ToRadio operations)
|
||||
public struct AdminMessage {
|
||||
public struct AdminMessage: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -487,6 +488,16 @@ public struct AdminMessage {
|
|||
set {payloadVariant = .commitEditSettings(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Add a contact (User) to the nodedb
|
||||
public var addContact: SharedContact {
|
||||
get {
|
||||
if case .addContact(let v)? = payloadVariant {return v}
|
||||
return SharedContact()
|
||||
}
|
||||
set {payloadVariant = .addContact(newValue)}
|
||||
}
|
||||
|
||||
///
|
||||
/// Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared.
|
||||
public var factoryResetDevice: Int32 {
|
||||
|
|
@ -563,7 +574,7 @@ public struct AdminMessage {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum OneOf_PayloadVariant: Equatable {
|
||||
public enum OneOf_PayloadVariant: Equatable, Sendable {
|
||||
///
|
||||
/// Send the specified channel in the response to this message
|
||||
/// NOTE: This field is sent with the channel index + 1 (to ensure we never try to send 'zero' - which protobufs treats as not present)
|
||||
|
|
@ -705,6 +716,9 @@ public struct AdminMessage {
|
|||
/// Commits an open transaction for any edits made to config, module config, owner, and channel settings
|
||||
case commitEditSettings(Bool)
|
||||
///
|
||||
/// Add a contact (User) to the nodedb
|
||||
case addContact(SharedContact)
|
||||
///
|
||||
/// Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared.
|
||||
case factoryResetDevice(Int32)
|
||||
///
|
||||
|
|
@ -728,225 +742,11 @@ public struct AdminMessage {
|
|||
/// Tell the node to reset the nodedb.
|
||||
case nodedbReset(Int32)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: AdminMessage.OneOf_PayloadVariant, rhs: AdminMessage.OneOf_PayloadVariant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.getChannelRequest, .getChannelRequest): return {
|
||||
guard case .getChannelRequest(let l) = lhs, case .getChannelRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getChannelResponse, .getChannelResponse): return {
|
||||
guard case .getChannelResponse(let l) = lhs, case .getChannelResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getOwnerRequest, .getOwnerRequest): return {
|
||||
guard case .getOwnerRequest(let l) = lhs, case .getOwnerRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getOwnerResponse, .getOwnerResponse): return {
|
||||
guard case .getOwnerResponse(let l) = lhs, case .getOwnerResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getConfigRequest, .getConfigRequest): return {
|
||||
guard case .getConfigRequest(let l) = lhs, case .getConfigRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getConfigResponse, .getConfigResponse): return {
|
||||
guard case .getConfigResponse(let l) = lhs, case .getConfigResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getModuleConfigRequest, .getModuleConfigRequest): return {
|
||||
guard case .getModuleConfigRequest(let l) = lhs, case .getModuleConfigRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getModuleConfigResponse, .getModuleConfigResponse): return {
|
||||
guard case .getModuleConfigResponse(let l) = lhs, case .getModuleConfigResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getCannedMessageModuleMessagesRequest, .getCannedMessageModuleMessagesRequest): return {
|
||||
guard case .getCannedMessageModuleMessagesRequest(let l) = lhs, case .getCannedMessageModuleMessagesRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getCannedMessageModuleMessagesResponse, .getCannedMessageModuleMessagesResponse): return {
|
||||
guard case .getCannedMessageModuleMessagesResponse(let l) = lhs, case .getCannedMessageModuleMessagesResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getDeviceMetadataRequest, .getDeviceMetadataRequest): return {
|
||||
guard case .getDeviceMetadataRequest(let l) = lhs, case .getDeviceMetadataRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getDeviceMetadataResponse, .getDeviceMetadataResponse): return {
|
||||
guard case .getDeviceMetadataResponse(let l) = lhs, case .getDeviceMetadataResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getRingtoneRequest, .getRingtoneRequest): return {
|
||||
guard case .getRingtoneRequest(let l) = lhs, case .getRingtoneRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getRingtoneResponse, .getRingtoneResponse): return {
|
||||
guard case .getRingtoneResponse(let l) = lhs, case .getRingtoneResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getDeviceConnectionStatusRequest, .getDeviceConnectionStatusRequest): return {
|
||||
guard case .getDeviceConnectionStatusRequest(let l) = lhs, case .getDeviceConnectionStatusRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getDeviceConnectionStatusResponse, .getDeviceConnectionStatusResponse): return {
|
||||
guard case .getDeviceConnectionStatusResponse(let l) = lhs, case .getDeviceConnectionStatusResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setHamMode, .setHamMode): return {
|
||||
guard case .setHamMode(let l) = lhs, case .setHamMode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getNodeRemoteHardwarePinsRequest, .getNodeRemoteHardwarePinsRequest): return {
|
||||
guard case .getNodeRemoteHardwarePinsRequest(let l) = lhs, case .getNodeRemoteHardwarePinsRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getNodeRemoteHardwarePinsResponse, .getNodeRemoteHardwarePinsResponse): return {
|
||||
guard case .getNodeRemoteHardwarePinsResponse(let l) = lhs, case .getNodeRemoteHardwarePinsResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.enterDfuModeRequest, .enterDfuModeRequest): return {
|
||||
guard case .enterDfuModeRequest(let l) = lhs, case .enterDfuModeRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.deleteFileRequest, .deleteFileRequest): return {
|
||||
guard case .deleteFileRequest(let l) = lhs, case .deleteFileRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setScale, .setScale): return {
|
||||
guard case .setScale(let l) = lhs, case .setScale(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.backupPreferences, .backupPreferences): return {
|
||||
guard case .backupPreferences(let l) = lhs, case .backupPreferences(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.restorePreferences, .restorePreferences): return {
|
||||
guard case .restorePreferences(let l) = lhs, case .restorePreferences(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.removeBackupPreferences, .removeBackupPreferences): return {
|
||||
guard case .removeBackupPreferences(let l) = lhs, case .removeBackupPreferences(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setOwner, .setOwner): return {
|
||||
guard case .setOwner(let l) = lhs, case .setOwner(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setChannel, .setChannel): return {
|
||||
guard case .setChannel(let l) = lhs, case .setChannel(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setConfig, .setConfig): return {
|
||||
guard case .setConfig(let l) = lhs, case .setConfig(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setModuleConfig, .setModuleConfig): return {
|
||||
guard case .setModuleConfig(let l) = lhs, case .setModuleConfig(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setCannedMessageModuleMessages, .setCannedMessageModuleMessages): return {
|
||||
guard case .setCannedMessageModuleMessages(let l) = lhs, case .setCannedMessageModuleMessages(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setRingtoneMessage, .setRingtoneMessage): return {
|
||||
guard case .setRingtoneMessage(let l) = lhs, case .setRingtoneMessage(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.removeByNodenum, .removeByNodenum): return {
|
||||
guard case .removeByNodenum(let l) = lhs, case .removeByNodenum(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setFavoriteNode, .setFavoriteNode): return {
|
||||
guard case .setFavoriteNode(let l) = lhs, case .setFavoriteNode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.removeFavoriteNode, .removeFavoriteNode): return {
|
||||
guard case .removeFavoriteNode(let l) = lhs, case .removeFavoriteNode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setFixedPosition, .setFixedPosition): return {
|
||||
guard case .setFixedPosition(let l) = lhs, case .setFixedPosition(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.removeFixedPosition, .removeFixedPosition): return {
|
||||
guard case .removeFixedPosition(let l) = lhs, case .removeFixedPosition(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setTimeOnly, .setTimeOnly): return {
|
||||
guard case .setTimeOnly(let l) = lhs, case .setTimeOnly(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getUiConfigRequest, .getUiConfigRequest): return {
|
||||
guard case .getUiConfigRequest(let l) = lhs, case .getUiConfigRequest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.getUiConfigResponse, .getUiConfigResponse): return {
|
||||
guard case .getUiConfigResponse(let l) = lhs, case .getUiConfigResponse(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.storeUiConfig, .storeUiConfig): return {
|
||||
guard case .storeUiConfig(let l) = lhs, case .storeUiConfig(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.setIgnoredNode, .setIgnoredNode): return {
|
||||
guard case .setIgnoredNode(let l) = lhs, case .setIgnoredNode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.removeIgnoredNode, .removeIgnoredNode): return {
|
||||
guard case .removeIgnoredNode(let l) = lhs, case .removeIgnoredNode(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.beginEditSettings, .beginEditSettings): return {
|
||||
guard case .beginEditSettings(let l) = lhs, case .beginEditSettings(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.commitEditSettings, .commitEditSettings): return {
|
||||
guard case .commitEditSettings(let l) = lhs, case .commitEditSettings(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.factoryResetDevice, .factoryResetDevice): return {
|
||||
guard case .factoryResetDevice(let l) = lhs, case .factoryResetDevice(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.rebootOtaSeconds, .rebootOtaSeconds): return {
|
||||
guard case .rebootOtaSeconds(let l) = lhs, case .rebootOtaSeconds(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.exitSimulator, .exitSimulator): return {
|
||||
guard case .exitSimulator(let l) = lhs, case .exitSimulator(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.rebootSeconds, .rebootSeconds): return {
|
||||
guard case .rebootSeconds(let l) = lhs, case .rebootSeconds(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.shutdownSeconds, .shutdownSeconds): return {
|
||||
guard case .shutdownSeconds(let l) = lhs, case .shutdownSeconds(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.factoryResetConfig, .factoryResetConfig): return {
|
||||
guard case .factoryResetConfig(let l) = lhs, case .factoryResetConfig(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.nodedbReset, .nodedbReset): return {
|
||||
guard case .nodedbReset(let l) = lhs, case .nodedbReset(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum ConfigType: SwiftProtobuf.Enum {
|
||||
public enum ConfigType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1026,11 +826,25 @@ public struct AdminMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.ConfigType] = [
|
||||
.deviceConfig,
|
||||
.positionConfig,
|
||||
.powerConfig,
|
||||
.networkConfig,
|
||||
.displayConfig,
|
||||
.loraConfig,
|
||||
.bluetoothConfig,
|
||||
.securityConfig,
|
||||
.sessionkeyConfig,
|
||||
.deviceuiConfig,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum ModuleConfigType: SwiftProtobuf.Enum {
|
||||
public enum ModuleConfigType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1128,9 +942,26 @@ public struct AdminMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.ModuleConfigType] = [
|
||||
.mqttConfig,
|
||||
.serialConfig,
|
||||
.extnotifConfig,
|
||||
.storeforwardConfig,
|
||||
.rangetestConfig,
|
||||
.telemetryConfig,
|
||||
.cannedmsgConfig,
|
||||
.audioConfig,
|
||||
.remotehardwareConfig,
|
||||
.neighborinfoConfig,
|
||||
.ambientlightingConfig,
|
||||
.detectionsensorConfig,
|
||||
.paxcounterConfig,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public enum BackupLocation: SwiftProtobuf.Enum {
|
||||
public enum BackupLocation: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1162,61 +993,20 @@ public struct AdminMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.BackupLocation] = [
|
||||
.flash,
|
||||
.sd,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension AdminMessage.ConfigType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.ConfigType] = [
|
||||
.deviceConfig,
|
||||
.positionConfig,
|
||||
.powerConfig,
|
||||
.networkConfig,
|
||||
.displayConfig,
|
||||
.loraConfig,
|
||||
.bluetoothConfig,
|
||||
.securityConfig,
|
||||
.sessionkeyConfig,
|
||||
.deviceuiConfig,
|
||||
]
|
||||
}
|
||||
|
||||
extension AdminMessage.ModuleConfigType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.ModuleConfigType] = [
|
||||
.mqttConfig,
|
||||
.serialConfig,
|
||||
.extnotifConfig,
|
||||
.storeforwardConfig,
|
||||
.rangetestConfig,
|
||||
.telemetryConfig,
|
||||
.cannedmsgConfig,
|
||||
.audioConfig,
|
||||
.remotehardwareConfig,
|
||||
.neighborinfoConfig,
|
||||
.ambientlightingConfig,
|
||||
.detectionsensorConfig,
|
||||
.paxcounterConfig,
|
||||
]
|
||||
}
|
||||
|
||||
extension AdminMessage.BackupLocation: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [AdminMessage.BackupLocation] = [
|
||||
.flash,
|
||||
.sd,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// Parameters for setting up Meshtastic for ameteur radio usage
|
||||
public struct HamParameters {
|
||||
public struct HamParameters: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1246,7 +1036,7 @@ public struct HamParameters {
|
|||
|
||||
///
|
||||
/// Response envelope for node_remote_hardware_pins
|
||||
public struct NodeRemoteHardwarePinsResponse {
|
||||
public struct NodeRemoteHardwarePinsResponse: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1260,15 +1050,32 @@ public struct NodeRemoteHardwarePinsResponse {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension AdminMessage: @unchecked Sendable {}
|
||||
extension AdminMessage.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension AdminMessage.ConfigType: @unchecked Sendable {}
|
||||
extension AdminMessage.ModuleConfigType: @unchecked Sendable {}
|
||||
extension AdminMessage.BackupLocation: @unchecked Sendable {}
|
||||
extension HamParameters: @unchecked Sendable {}
|
||||
extension NodeRemoteHardwarePinsResponse: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
public struct SharedContact: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// The node number of the contact
|
||||
public var nodeNum: UInt32 = 0
|
||||
|
||||
///
|
||||
/// The User of the contact
|
||||
public var user: User {
|
||||
get {return _user ?? User()}
|
||||
set {_user = newValue}
|
||||
}
|
||||
/// Returns true if `user` has been explicitly set.
|
||||
public var hasUser: Bool {return self._user != nil}
|
||||
/// Clears the value of `user`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearUser() {self._user = nil}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
||||
fileprivate var _user: User? = nil
|
||||
}
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
|
|
@ -1322,6 +1129,7 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
48: .standard(proto: "remove_ignored_node"),
|
||||
64: .standard(proto: "begin_edit_settings"),
|
||||
65: .standard(proto: "commit_edit_settings"),
|
||||
66: .standard(proto: "add_contact"),
|
||||
94: .standard(proto: "factory_reset_device"),
|
||||
95: .standard(proto: "reboot_ota_seconds"),
|
||||
96: .standard(proto: "exit_simulator"),
|
||||
|
|
@ -1764,6 +1572,19 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
self.payloadVariant = .commitEditSettings(v)
|
||||
}
|
||||
}()
|
||||
case 66: try {
|
||||
var v: SharedContact?
|
||||
var hadOneofValue = false
|
||||
if let current = self.payloadVariant {
|
||||
hadOneofValue = true
|
||||
if case .addContact(let m) = current {v = m}
|
||||
}
|
||||
try decoder.decodeSingularMessageField(value: &v)
|
||||
if let v = v {
|
||||
if hadOneofValue {try decoder.handleConflictingOneOf()}
|
||||
self.payloadVariant = .addContact(v)
|
||||
}
|
||||
}()
|
||||
case 94: try {
|
||||
var v: Int32?
|
||||
try decoder.decodeSingularInt32Field(value: &v)
|
||||
|
|
@ -2008,6 +1829,10 @@ extension AdminMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
guard case .commitEditSettings(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 65)
|
||||
}()
|
||||
case .addContact?: try {
|
||||
guard case .addContact(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 66)
|
||||
}()
|
||||
case .factoryResetDevice?: try {
|
||||
guard case .factoryResetDevice(let v)? = self.payloadVariant else { preconditionFailure() }
|
||||
try visitor.visitSingularInt32Field(value: v, fieldNumber: 94)
|
||||
|
|
@ -2123,7 +1948,7 @@ extension HamParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
if self.txPower != 0 {
|
||||
try visitor.visitSingularInt32Field(value: self.txPower, fieldNumber: 2)
|
||||
}
|
||||
if self.frequency != 0 {
|
||||
if self.frequency.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.frequency, fieldNumber: 3)
|
||||
}
|
||||
if !self.shortName.isEmpty {
|
||||
|
|
@ -2173,3 +1998,45 @@ extension NodeRemoteHardwarePinsResponse: SwiftProtobuf.Message, SwiftProtobuf._
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension SharedContact: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
|
||||
public static let protoMessageName: String = _protobuf_package + ".SharedContact"
|
||||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "node_num"),
|
||||
2: .same(proto: "user"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.nodeNum) }()
|
||||
case 2: try { try decoder.decodeSingularMessageField(value: &self._user) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if self.nodeNum != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.nodeNum, fieldNumber: 1)
|
||||
}
|
||||
try { if let v = self._user {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: SharedContact, rhs: SharedContact) -> Bool {
|
||||
if lhs.nodeNum != rhs.nodeNum {return false}
|
||||
if lhs._user != rhs._user {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/apponly.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -26,7 +26,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
/// any SECONDARY channels.
|
||||
/// No DISABLED channels are included.
|
||||
/// This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL
|
||||
public struct ChannelSet {
|
||||
public struct ChannelSet: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -53,10 +53,6 @@ public struct ChannelSet {
|
|||
fileprivate var _loraConfig: Config.LoRaConfig? = nil
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension ChannelSet: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/atak.proto
|
||||
|
|
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public enum Team: SwiftProtobuf.Enum {
|
||||
public enum Team: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -130,11 +131,6 @@ public enum Team: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension Team: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Team] = [
|
||||
.unspecifedColor,
|
||||
|
|
@ -153,13 +149,12 @@ extension Team: CaseIterable {
|
|||
.darkGreen,
|
||||
.brown,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
///
|
||||
/// Role of the group member
|
||||
public enum MemberRole: SwiftProtobuf.Enum {
|
||||
public enum MemberRole: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -233,11 +228,6 @@ public enum MemberRole: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension MemberRole: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [MemberRole] = [
|
||||
.unspecifed,
|
||||
|
|
@ -250,13 +240,12 @@ extension MemberRole: CaseIterable {
|
|||
.rto,
|
||||
.k9,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
///
|
||||
/// Packets for the official ATAK Plugin
|
||||
public struct TAKPacket {
|
||||
public struct TAKPacket: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -337,7 +326,7 @@ public struct TAKPacket {
|
|||
|
||||
///
|
||||
/// The payload of the packet
|
||||
public enum OneOf_PayloadVariant: Equatable {
|
||||
public enum OneOf_PayloadVariant: Equatable, @unchecked Sendable {
|
||||
///
|
||||
/// TAK position report
|
||||
case pli(PLI)
|
||||
|
|
@ -349,28 +338,6 @@ public struct TAKPacket {
|
|||
/// May be compressed / truncated by the sender (EUD)
|
||||
case detail(Data)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: TAKPacket.OneOf_PayloadVariant, rhs: TAKPacket.OneOf_PayloadVariant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.pli, .pli): return {
|
||||
guard case .pli(let l) = lhs, case .pli(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.chat, .chat): return {
|
||||
guard case .chat(let l) = lhs, case .chat(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.detail, .detail): return {
|
||||
guard case .detail(let l) = lhs, case .detail(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -382,7 +349,7 @@ public struct TAKPacket {
|
|||
|
||||
///
|
||||
/// ATAK GeoChat message
|
||||
public struct GeoChat {
|
||||
public struct GeoChat: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -424,7 +391,7 @@ public struct GeoChat {
|
|||
///
|
||||
/// ATAK Group
|
||||
/// <__group role='Team Member' name='Cyan'/>
|
||||
public struct Group {
|
||||
public struct Group: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -446,7 +413,7 @@ public struct Group {
|
|||
///
|
||||
/// ATAK EUD Status
|
||||
/// <status battery='100' />
|
||||
public struct Status {
|
||||
public struct Status: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -463,7 +430,7 @@ public struct Status {
|
|||
///
|
||||
/// ATAK Contact
|
||||
/// <contact endpoint='0.0.0.0:4242:tcp' phone='+12345678' callsign='FALKE'/>
|
||||
public struct Contact {
|
||||
public struct Contact: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -483,7 +450,7 @@ public struct Contact {
|
|||
|
||||
///
|
||||
/// Position Location Information from ATAK
|
||||
public struct PLI {
|
||||
public struct PLI: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -515,18 +482,6 @@ public struct PLI {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension Team: @unchecked Sendable {}
|
||||
extension MemberRole: @unchecked Sendable {}
|
||||
extension TAKPacket: @unchecked Sendable {}
|
||||
extension TAKPacket.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension GeoChat: @unchecked Sendable {}
|
||||
extension Group: @unchecked Sendable {}
|
||||
extension Status: @unchecked Sendable {}
|
||||
extension Contact: @unchecked Sendable {}
|
||||
extension PLI: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/cannedmessages.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// Canned message module configuration.
|
||||
public struct CannedMessageModuleConfig {
|
||||
public struct CannedMessageModuleConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -36,10 +36,6 @@ public struct CannedMessageModuleConfig {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension CannedMessageModuleConfig: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/channel.proto
|
||||
|
|
@ -36,13 +37,15 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
/// FIXME: Add description of multi-channel support and how primary vs secondary channels are used.
|
||||
/// FIXME: explain how apps use channels for security.
|
||||
/// explain how remote settings and remote gpio are managed as an example
|
||||
public struct ChannelSettings {
|
||||
public struct ChannelSettings: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// Deprecated in favor of LoraConfig.channel_num
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var channelNum: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
@ -111,7 +114,7 @@ public struct ChannelSettings {
|
|||
|
||||
///
|
||||
/// This message is specifically for modules to store per-channel configuration data.
|
||||
public struct ModuleSettings {
|
||||
public struct ModuleSettings: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -132,7 +135,7 @@ public struct ModuleSettings {
|
|||
|
||||
///
|
||||
/// A pair of a channel number, mode and the (sharable) settings for that channel
|
||||
public struct Channel {
|
||||
public struct Channel: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -170,7 +173,7 @@ public struct Channel {
|
|||
/// cross band routing as needed.
|
||||
/// If a device has only a single radio (the common case) only one channel can be PRIMARY at a time
|
||||
/// (but any number of SECONDARY channels can't be sent received on that common frequency)
|
||||
public enum Role: SwiftProtobuf.Enum {
|
||||
public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -209,6 +212,13 @@ public struct Channel {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Channel.Role] = [
|
||||
.disabled,
|
||||
.primary,
|
||||
.secondary,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -216,26 +226,6 @@ public struct Channel {
|
|||
fileprivate var _settings: ChannelSettings? = nil
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension Channel.Role: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Channel.Role] = [
|
||||
.disabled,
|
||||
.primary,
|
||||
.secondary,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension ChannelSettings: @unchecked Sendable {}
|
||||
extension ModuleSettings: @unchecked Sendable {}
|
||||
extension Channel: @unchecked Sendable {}
|
||||
extension Channel.Role: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/clientonly.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -23,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
///
|
||||
/// This abstraction is used to contain any configuration for provisioning a node on any client.
|
||||
/// It is useful for importing and exporting configurations.
|
||||
public struct DeviceProfile {
|
||||
public struct DeviceProfile: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -130,10 +130,6 @@ public struct DeviceProfile {
|
|||
fileprivate var _cannedMessages: String? = nil
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension DeviceProfile: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/config.proto
|
||||
|
|
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public struct Config {
|
||||
public struct Config: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -113,7 +114,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Payload Variant
|
||||
public enum OneOf_PayloadVariant: Equatable {
|
||||
public enum OneOf_PayloadVariant: Equatable, Sendable {
|
||||
case device(Config.DeviceConfig)
|
||||
case position(Config.PositionConfig)
|
||||
case power(Config.PowerConfig)
|
||||
|
|
@ -125,61 +126,11 @@ public struct Config {
|
|||
case sessionkey(Config.SessionkeyConfig)
|
||||
case deviceUi(DeviceUIConfig)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: Config.OneOf_PayloadVariant, rhs: Config.OneOf_PayloadVariant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.device, .device): return {
|
||||
guard case .device(let l) = lhs, case .device(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.position, .position): return {
|
||||
guard case .position(let l) = lhs, case .position(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.power, .power): return {
|
||||
guard case .power(let l) = lhs, case .power(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.network, .network): return {
|
||||
guard case .network(let l) = lhs, case .network(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.display, .display): return {
|
||||
guard case .display(let l) = lhs, case .display(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.lora, .lora): return {
|
||||
guard case .lora(let l) = lhs, case .lora(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.bluetooth, .bluetooth): return {
|
||||
guard case .bluetooth(let l) = lhs, case .bluetooth(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.security, .security): return {
|
||||
guard case .security(let l) = lhs, case .security(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.sessionkey, .sessionkey): return {
|
||||
guard case .sessionkey(let l) = lhs, case .sessionkey(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.deviceUi, .deviceUi): return {
|
||||
guard case .deviceUi(let l) = lhs, case .deviceUi(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// Configuration
|
||||
public struct DeviceConfig {
|
||||
public struct DeviceConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -191,6 +142,8 @@ public struct Config {
|
|||
///
|
||||
/// Disabling this will disable the SerialConsole by not initilizing the StreamAPI
|
||||
/// Moved to SecurityConfig
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var serialEnabled: Bool = false
|
||||
|
||||
///
|
||||
|
|
@ -220,6 +173,8 @@ public struct Config {
|
|||
/// If true, device is considered to be "managed" by a mesh administrator
|
||||
/// Clients should then limit available configuration and administrative options inside the user interface
|
||||
/// Moved to SecurityConfig
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var isManaged: Bool = false
|
||||
|
||||
///
|
||||
|
|
@ -238,7 +193,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Defines the device's role on the Mesh network
|
||||
public enum Role: SwiftProtobuf.Enum {
|
||||
public enum Role: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -256,6 +211,8 @@ public struct Config {
|
|||
/// The wifi radio and the oled screen will be put to sleep.
|
||||
/// This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh.
|
||||
case router // = 2
|
||||
|
||||
/// NOTE: This enum value was marked as deprecated in the .proto file
|
||||
case routerClient // = 3
|
||||
|
||||
///
|
||||
|
|
@ -356,11 +313,27 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DeviceConfig.Role] = [
|
||||
.client,
|
||||
.clientMute,
|
||||
.router,
|
||||
.routerClient,
|
||||
.repeater,
|
||||
.tracker,
|
||||
.sensor,
|
||||
.tak,
|
||||
.clientHidden,
|
||||
.lostAndFound,
|
||||
.takTracker,
|
||||
.routerLate,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Defines the device's behavior for how messages are rebroadcast
|
||||
public enum RebroadcastMode: SwiftProtobuf.Enum {
|
||||
public enum RebroadcastMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -421,6 +394,16 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [
|
||||
.all,
|
||||
.allSkipDecoding,
|
||||
.localOnly,
|
||||
.knownOnly,
|
||||
.none,
|
||||
.corePortnumsOnly,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -428,7 +411,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Position Config
|
||||
public struct PositionConfig {
|
||||
public struct PositionConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -450,6 +433,8 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Is GPS enabled for this node?
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var gpsEnabled: Bool = false
|
||||
|
||||
///
|
||||
|
|
@ -460,6 +445,8 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Deprecated in favor of using smart / regular broadcast intervals as implicit attempt time
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var gpsAttemptTime: UInt32 = 0
|
||||
|
||||
///
|
||||
|
|
@ -500,7 +487,7 @@ public struct Config {
|
|||
/// are always included (also time if GPS-synced)
|
||||
/// NOTE: the more fields are included, the larger the message will be -
|
||||
/// leading to longer airtime and a higher risk of packet loss
|
||||
public enum PositionFlags: SwiftProtobuf.Enum {
|
||||
public enum PositionFlags: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -590,9 +577,24 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.PositionConfig.PositionFlags] = [
|
||||
.unset,
|
||||
.altitude,
|
||||
.altitudeMsl,
|
||||
.geoidalSeparation,
|
||||
.dop,
|
||||
.hvdop,
|
||||
.satinview,
|
||||
.seqNo,
|
||||
.timestamp,
|
||||
.heading,
|
||||
.speed,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public enum GpsMode: SwiftProtobuf.Enum {
|
||||
public enum GpsMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -630,6 +632,13 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.PositionConfig.GpsMode] = [
|
||||
.disabled,
|
||||
.enabled,
|
||||
.notPresent,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -638,7 +647,7 @@ public struct Config {
|
|||
///
|
||||
/// Power Config\
|
||||
/// See [Power Config](/docs/settings/config/power) for additional power config details.
|
||||
public struct PowerConfig {
|
||||
public struct PowerConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -698,7 +707,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Network Config
|
||||
public struct NetworkConfig {
|
||||
public struct NetworkConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -749,7 +758,7 @@ public struct Config {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum AddressMode: SwiftProtobuf.Enum {
|
||||
public enum AddressMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -781,11 +790,17 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.NetworkConfig.AddressMode] = [
|
||||
.dhcp,
|
||||
.static,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Available flags auxiliary network protocols
|
||||
public enum ProtocolFlags: SwiftProtobuf.Enum {
|
||||
public enum ProtocolFlags: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -817,9 +832,15 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [
|
||||
.noBroadcast,
|
||||
.udpBroadcast,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public struct IpV4Config {
|
||||
public struct IpV4Config: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -852,7 +873,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Display Config
|
||||
public struct DisplayConfig {
|
||||
public struct DisplayConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -913,7 +934,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// How the GPS coordinates are displayed on the OLED screen.
|
||||
public enum GpsCoordinateFormat: SwiftProtobuf.Enum {
|
||||
public enum GpsCoordinateFormat: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -976,11 +997,21 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [
|
||||
.dec,
|
||||
.dms,
|
||||
.utm,
|
||||
.mgrs,
|
||||
.olc,
|
||||
.osgr,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Unit display preference
|
||||
public enum DisplayUnits: SwiftProtobuf.Enum {
|
||||
public enum DisplayUnits: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1012,11 +1043,17 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.DisplayUnits] = [
|
||||
.metric,
|
||||
.imperial,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Override OLED outo detect with this if it fails.
|
||||
public enum OledType: SwiftProtobuf.Enum {
|
||||
public enum OledType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1066,9 +1103,18 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.OledType] = [
|
||||
.oledAuto,
|
||||
.oledSsd1306,
|
||||
.oledSh1106,
|
||||
.oledSh1107,
|
||||
.oledSh110712864,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public enum DisplayMode: SwiftProtobuf.Enum {
|
||||
public enum DisplayMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1112,9 +1158,17 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.DisplayMode] = [
|
||||
.default,
|
||||
.twocolor,
|
||||
.inverted,
|
||||
.color,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public enum CompassOrientation: SwiftProtobuf.Enum {
|
||||
public enum CompassOrientation: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1182,6 +1236,18 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.CompassOrientation] = [
|
||||
.degrees0,
|
||||
.degrees90,
|
||||
.degrees180,
|
||||
.degrees270,
|
||||
.degrees0Inverted,
|
||||
.degrees90Inverted,
|
||||
.degrees180Inverted,
|
||||
.degrees270Inverted,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -1189,7 +1255,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Lora Config
|
||||
public struct LoRaConfig {
|
||||
public struct LoRaConfig: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1353,7 +1419,7 @@ public struct Config {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum RegionCode: SwiftProtobuf.Enum {
|
||||
public enum RegionCode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1505,12 +1571,38 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.LoRaConfig.RegionCode] = [
|
||||
.unset,
|
||||
.us,
|
||||
.eu433,
|
||||
.eu868,
|
||||
.cn,
|
||||
.jp,
|
||||
.anz,
|
||||
.kr,
|
||||
.tw,
|
||||
.ru,
|
||||
.in,
|
||||
.nz865,
|
||||
.th,
|
||||
.lora24,
|
||||
.ua433,
|
||||
.ua868,
|
||||
.my433,
|
||||
.my919,
|
||||
.sg923,
|
||||
.ph433,
|
||||
.ph868,
|
||||
.ph915,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// Standard predefined channel settings
|
||||
/// Note: these mappings must match ModemPreset Choice in the device code.
|
||||
public enum ModemPreset: SwiftProtobuf.Enum {
|
||||
public enum ModemPreset: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1524,6 +1616,8 @@ public struct Config {
|
|||
///
|
||||
/// Very Long Range - Slow
|
||||
/// Deprecated in 2.5: Works only with txco and is unusably slow
|
||||
///
|
||||
/// NOTE: This enum value was marked as deprecated in the .proto file
|
||||
case veryLongSlow // = 2
|
||||
|
||||
///
|
||||
|
|
@ -1587,6 +1681,19 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.LoRaConfig.ModemPreset] = [
|
||||
.longFast,
|
||||
.longSlow,
|
||||
.veryLongSlow,
|
||||
.mediumSlow,
|
||||
.mediumFast,
|
||||
.shortSlow,
|
||||
.shortFast,
|
||||
.longModerate,
|
||||
.shortTurbo,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -1594,7 +1701,7 @@ public struct Config {
|
|||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
public struct BluetoothConfig {
|
||||
public struct BluetoothConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1613,7 +1720,7 @@ public struct Config {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum PairingMode: SwiftProtobuf.Enum {
|
||||
public enum PairingMode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1651,12 +1758,19 @@ public struct Config {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.BluetoothConfig.PairingMode] = [
|
||||
.randomPin,
|
||||
.fixedPin,
|
||||
.noPin,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public struct SecurityConfig {
|
||||
public struct SecurityConfig: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1700,7 +1814,7 @@ public struct Config {
|
|||
|
||||
///
|
||||
/// Blank config request, strictly for getting the session key
|
||||
public struct SessionkeyConfig {
|
||||
public struct SessionkeyConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1713,218 +1827,6 @@ public struct Config {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension Config.DeviceConfig.Role: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DeviceConfig.Role] = [
|
||||
.client,
|
||||
.clientMute,
|
||||
.router,
|
||||
.routerClient,
|
||||
.repeater,
|
||||
.tracker,
|
||||
.sensor,
|
||||
.tak,
|
||||
.clientHidden,
|
||||
.lostAndFound,
|
||||
.takTracker,
|
||||
.routerLate,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DeviceConfig.RebroadcastMode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DeviceConfig.RebroadcastMode] = [
|
||||
.all,
|
||||
.allSkipDecoding,
|
||||
.localOnly,
|
||||
.knownOnly,
|
||||
.none,
|
||||
.corePortnumsOnly,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.PositionConfig.PositionFlags: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.PositionConfig.PositionFlags] = [
|
||||
.unset,
|
||||
.altitude,
|
||||
.altitudeMsl,
|
||||
.geoidalSeparation,
|
||||
.dop,
|
||||
.hvdop,
|
||||
.satinview,
|
||||
.seqNo,
|
||||
.timestamp,
|
||||
.heading,
|
||||
.speed,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.PositionConfig.GpsMode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.PositionConfig.GpsMode] = [
|
||||
.disabled,
|
||||
.enabled,
|
||||
.notPresent,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.NetworkConfig.AddressMode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.NetworkConfig.AddressMode] = [
|
||||
.dhcp,
|
||||
.static,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.NetworkConfig.ProtocolFlags: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.NetworkConfig.ProtocolFlags] = [
|
||||
.noBroadcast,
|
||||
.udpBroadcast,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.GpsCoordinateFormat: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.GpsCoordinateFormat] = [
|
||||
.dec,
|
||||
.dms,
|
||||
.utm,
|
||||
.mgrs,
|
||||
.olc,
|
||||
.osgr,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.DisplayUnits: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.DisplayUnits] = [
|
||||
.metric,
|
||||
.imperial,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.OledType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.OledType] = [
|
||||
.oledAuto,
|
||||
.oledSsd1306,
|
||||
.oledSh1106,
|
||||
.oledSh1107,
|
||||
.oledSh110712864,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.DisplayMode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.DisplayMode] = [
|
||||
.default,
|
||||
.twocolor,
|
||||
.inverted,
|
||||
.color,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.DisplayConfig.CompassOrientation: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.DisplayConfig.CompassOrientation] = [
|
||||
.degrees0,
|
||||
.degrees90,
|
||||
.degrees180,
|
||||
.degrees270,
|
||||
.degrees0Inverted,
|
||||
.degrees90Inverted,
|
||||
.degrees180Inverted,
|
||||
.degrees270Inverted,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.LoRaConfig.RegionCode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.LoRaConfig.RegionCode] = [
|
||||
.unset,
|
||||
.us,
|
||||
.eu433,
|
||||
.eu868,
|
||||
.cn,
|
||||
.jp,
|
||||
.anz,
|
||||
.kr,
|
||||
.tw,
|
||||
.ru,
|
||||
.in,
|
||||
.nz865,
|
||||
.th,
|
||||
.lora24,
|
||||
.ua433,
|
||||
.ua868,
|
||||
.my433,
|
||||
.my919,
|
||||
.sg923,
|
||||
.ph433,
|
||||
.ph868,
|
||||
.ph915,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.LoRaConfig.ModemPreset: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.LoRaConfig.ModemPreset] = [
|
||||
.longFast,
|
||||
.longSlow,
|
||||
.veryLongSlow,
|
||||
.mediumSlow,
|
||||
.mediumFast,
|
||||
.shortSlow,
|
||||
.shortFast,
|
||||
.longModerate,
|
||||
.shortTurbo,
|
||||
]
|
||||
}
|
||||
|
||||
extension Config.BluetoothConfig.PairingMode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Config.BluetoothConfig.PairingMode] = [
|
||||
.randomPin,
|
||||
.fixedPin,
|
||||
.noPin,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension Config: @unchecked Sendable {}
|
||||
extension Config.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension Config.DeviceConfig: @unchecked Sendable {}
|
||||
extension Config.DeviceConfig.Role: @unchecked Sendable {}
|
||||
extension Config.DeviceConfig.RebroadcastMode: @unchecked Sendable {}
|
||||
extension Config.PositionConfig: @unchecked Sendable {}
|
||||
extension Config.PositionConfig.PositionFlags: @unchecked Sendable {}
|
||||
extension Config.PositionConfig.GpsMode: @unchecked Sendable {}
|
||||
extension Config.PowerConfig: @unchecked Sendable {}
|
||||
extension Config.NetworkConfig: @unchecked Sendable {}
|
||||
extension Config.NetworkConfig.AddressMode: @unchecked Sendable {}
|
||||
extension Config.NetworkConfig.ProtocolFlags: @unchecked Sendable {}
|
||||
extension Config.NetworkConfig.IpV4Config: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.GpsCoordinateFormat: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.DisplayUnits: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.OledType: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.DisplayMode: @unchecked Sendable {}
|
||||
extension Config.DisplayConfig.CompassOrientation: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig.RegionCode: @unchecked Sendable {}
|
||||
extension Config.LoRaConfig.ModemPreset: @unchecked Sendable {}
|
||||
extension Config.BluetoothConfig: @unchecked Sendable {}
|
||||
extension Config.BluetoothConfig.PairingMode: @unchecked Sendable {}
|
||||
extension Config.SecurityConfig: @unchecked Sendable {}
|
||||
extension Config.SessionkeyConfig: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -2432,7 +2334,7 @@ extension Config.PowerConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if self.onBatteryShutdownAfterSecs != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.onBatteryShutdownAfterSecs, fieldNumber: 2)
|
||||
}
|
||||
if self.adcMultiplierOverride != 0 {
|
||||
if self.adcMultiplierOverride.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.adcMultiplierOverride, fieldNumber: 3)
|
||||
}
|
||||
if self.waitBluetoothSecs != 0 {
|
||||
|
|
@ -2900,7 +2802,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if _storage._codingRate != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: _storage._codingRate, fieldNumber: 5)
|
||||
}
|
||||
if _storage._frequencyOffset != 0 {
|
||||
if _storage._frequencyOffset.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: _storage._frequencyOffset, fieldNumber: 6)
|
||||
}
|
||||
if _storage._region != .unset {
|
||||
|
|
@ -2924,7 +2826,7 @@ extension Config.LoRaConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
|
|||
if _storage._sx126XRxBoostedGain != false {
|
||||
try visitor.visitSingularBoolField(value: _storage._sx126XRxBoostedGain, fieldNumber: 13)
|
||||
}
|
||||
if _storage._overrideFrequency != 0 {
|
||||
if _storage._overrideFrequency.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: _storage._overrideFrequency, fieldNumber: 14)
|
||||
}
|
||||
if _storage._paFanDisabled != false {
|
||||
|
|
@ -3141,8 +3043,8 @@ extension Config.SessionkeyConfig: SwiftProtobuf.Message, SwiftProtobuf._Message
|
|||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let _ = try decoder.nextFieldNumber() {
|
||||
}
|
||||
// Load everything into unknown fields
|
||||
while try decoder.nextFieldNumber() != nil {}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/connection_status.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public struct DeviceConnectionStatus {
|
||||
public struct DeviceConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -81,7 +81,7 @@ public struct DeviceConnectionStatus {
|
|||
|
||||
///
|
||||
/// WiFi connection status
|
||||
public struct WifiConnectionStatus {
|
||||
public struct WifiConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -114,7 +114,7 @@ public struct WifiConnectionStatus {
|
|||
|
||||
///
|
||||
/// Ethernet connection status
|
||||
public struct EthernetConnectionStatus {
|
||||
public struct EthernetConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -139,7 +139,7 @@ public struct EthernetConnectionStatus {
|
|||
|
||||
///
|
||||
/// Ethernet or WiFi connection status
|
||||
public struct NetworkConnectionStatus {
|
||||
public struct NetworkConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -167,7 +167,7 @@ public struct NetworkConnectionStatus {
|
|||
|
||||
///
|
||||
/// Bluetooth connection status
|
||||
public struct BluetoothConnectionStatus {
|
||||
public struct BluetoothConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -191,7 +191,7 @@ public struct BluetoothConnectionStatus {
|
|||
|
||||
///
|
||||
/// Serial connection status
|
||||
public struct SerialConnectionStatus {
|
||||
public struct SerialConnectionStatus: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -209,15 +209,6 @@ public struct SerialConnectionStatus {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension DeviceConnectionStatus: @unchecked Sendable {}
|
||||
extension WifiConnectionStatus: @unchecked Sendable {}
|
||||
extension EthernetConnectionStatus: @unchecked Sendable {}
|
||||
extension NetworkConnectionStatus: @unchecked Sendable {}
|
||||
extension BluetoothConnectionStatus: @unchecked Sendable {}
|
||||
extension SerialConnectionStatus: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/device_ui.proto
|
||||
|
|
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public enum Theme: SwiftProtobuf.Enum {
|
||||
public enum Theme: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -58,24 +59,18 @@ public enum Theme: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension Theme: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Theme] = [
|
||||
.dark,
|
||||
.light,
|
||||
.red,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
///
|
||||
/// Localization
|
||||
public enum Language: SwiftProtobuf.Enum {
|
||||
public enum Language: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -209,11 +204,6 @@ public enum Language: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension Language: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [Language] = [
|
||||
.english,
|
||||
|
|
@ -236,11 +226,10 @@ extension Language: CaseIterable {
|
|||
.simplifiedChinese,
|
||||
.traditionalChinese,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
public struct DeviceUIConfig {
|
||||
public struct DeviceUIConfig: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -361,7 +350,7 @@ public struct DeviceUIConfig {
|
|||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
public struct NodeFilter {
|
||||
public struct NodeFilter: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -399,7 +388,7 @@ public struct NodeFilter {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
public struct NodeHighlight {
|
||||
public struct NodeHighlight: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -429,7 +418,7 @@ public struct NodeHighlight {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
public struct GeoPoint {
|
||||
public struct GeoPoint: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -451,7 +440,7 @@ public struct GeoPoint {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
public struct Map {
|
||||
public struct Map: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -482,16 +471,6 @@ public struct Map {
|
|||
fileprivate var _home: GeoPoint? = nil
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension Theme: @unchecked Sendable {}
|
||||
extension Language: @unchecked Sendable {}
|
||||
extension DeviceUIConfig: @unchecked Sendable {}
|
||||
extension NodeFilter: @unchecked Sendable {}
|
||||
extension NodeHighlight: @unchecked Sendable {}
|
||||
extension GeoPoint: @unchecked Sendable {}
|
||||
extension Map: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/deviceonly.proto
|
||||
|
|
@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// Position with static location information only for NodeDBLite
|
||||
public struct PositionLite {
|
||||
public struct PositionLite: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -57,13 +58,15 @@ public struct PositionLite {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
public struct UserLite {
|
||||
public struct UserLite: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
///
|
||||
/// This is the addr of the radio.
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var macaddr: Data = Data()
|
||||
|
||||
///
|
||||
|
|
@ -97,12 +100,25 @@ public struct UserLite {
|
|||
/// This is sent out to other nodes on the mesh to allow them to compute a shared secret key.
|
||||
public var publicKey: Data = Data()
|
||||
|
||||
///
|
||||
/// Whether or not the node can be messaged
|
||||
public var isUnmessagable: Bool {
|
||||
get {return _isUnmessagable ?? false}
|
||||
set {_isUnmessagable = newValue}
|
||||
}
|
||||
/// Returns true if `isUnmessagable` has been explicitly set.
|
||||
public var hasIsUnmessagable: Bool {return self._isUnmessagable != nil}
|
||||
/// Clears the value of `isUnmessagable`. Subsequent reads from it will return its default value.
|
||||
public mutating func clearIsUnmessagable() {self._isUnmessagable = nil}
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
||||
fileprivate var _isUnmessagable: Bool? = nil
|
||||
}
|
||||
|
||||
public struct NodeInfoLite {
|
||||
public struct NodeInfoLite: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -224,7 +240,7 @@ public struct NodeInfoLite {
|
|||
/// FIXME, since we write this each time we enter deep sleep (and have infinite
|
||||
/// flash) it would be better to use some sort of append only data structure for
|
||||
/// the receive queue and use the preferences store for the other stuff
|
||||
public struct DeviceState {
|
||||
public struct DeviceState: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -284,6 +300,8 @@ public struct DeviceState {
|
|||
/// Used only during development.
|
||||
/// Indicates developer is testing and changes should never be saved to flash.
|
||||
/// Deprecated in 2.3.1
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var noSave: Bool {
|
||||
get {return _storage._noSave}
|
||||
set {_uniqueStorage()._noSave = newValue}
|
||||
|
|
@ -292,6 +310,8 @@ public struct DeviceState {
|
|||
///
|
||||
/// Previously used to manage GPS factory resets.
|
||||
/// Deprecated in 2.5.23
|
||||
///
|
||||
/// NOTE: This field was marked as deprecated in the .proto file.
|
||||
public var didGpsReset: Bool {
|
||||
get {return _storage._didGpsReset}
|
||||
set {_uniqueStorage()._didGpsReset = newValue}
|
||||
|
|
@ -324,7 +344,7 @@ public struct DeviceState {
|
|||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
public struct NodeDatabase {
|
||||
public struct NodeDatabase: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -346,7 +366,7 @@ public struct NodeDatabase {
|
|||
|
||||
///
|
||||
/// The on-disk saved channels
|
||||
public struct ChannelFile {
|
||||
public struct ChannelFile: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -368,7 +388,7 @@ public struct ChannelFile {
|
|||
|
||||
///
|
||||
/// The on-disk backup of the node's preferences
|
||||
public struct BackupPreferences {
|
||||
public struct BackupPreferences: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -435,16 +455,6 @@ public struct BackupPreferences {
|
|||
fileprivate var _owner: User? = nil
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension PositionLite: @unchecked Sendable {}
|
||||
extension UserLite: @unchecked Sendable {}
|
||||
extension NodeInfoLite: @unchecked Sendable {}
|
||||
extension DeviceState: @unchecked Sendable {}
|
||||
extension NodeDatabase: @unchecked Sendable {}
|
||||
extension ChannelFile: @unchecked Sendable {}
|
||||
extension BackupPreferences: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -515,6 +525,7 @@ extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
5: .standard(proto: "is_licensed"),
|
||||
6: .same(proto: "role"),
|
||||
7: .standard(proto: "public_key"),
|
||||
9: .standard(proto: "is_unmessagable"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -530,12 +541,17 @@ extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
case 5: try { try decoder.decodeSingularBoolField(value: &self.isLicensed) }()
|
||||
case 6: try { try decoder.decodeSingularEnumField(value: &self.role) }()
|
||||
case 7: try { try decoder.decodeSingularBytesField(value: &self.publicKey) }()
|
||||
case 9: try { try decoder.decodeSingularBoolField(value: &self._isUnmessagable) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every if/case branch local when no optimizations
|
||||
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
|
||||
// https://github.com/apple/swift-protobuf/issues/1182
|
||||
if !self.macaddr.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.macaddr, fieldNumber: 1)
|
||||
}
|
||||
|
|
@ -557,6 +573,9 @@ extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if !self.publicKey.isEmpty {
|
||||
try visitor.visitSingularBytesField(value: self.publicKey, fieldNumber: 7)
|
||||
}
|
||||
try { if let v = self._isUnmessagable {
|
||||
try visitor.visitSingularBoolField(value: v, fieldNumber: 9)
|
||||
} }()
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -568,6 +587,7 @@ extension UserLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
if lhs.isLicensed != rhs.isLicensed {return false}
|
||||
if lhs.role != rhs.role {return false}
|
||||
if lhs.publicKey != rhs.publicKey {return false}
|
||||
if lhs._isUnmessagable != rhs._isUnmessagable {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -680,7 +700,7 @@ extension NodeInfoLite: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementat
|
|||
try { if let v = _storage._position {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 3)
|
||||
} }()
|
||||
if _storage._snr != 0 {
|
||||
if _storage._snr.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: _storage._snr, fieldNumber: 4)
|
||||
}
|
||||
if _storage._lastHeard != 0 {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/interdevice.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public enum MessageType: SwiftProtobuf.Enum {
|
||||
public enum MessageType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case ack // = 0
|
||||
|
||||
|
|
@ -82,11 +82,6 @@ public enum MessageType: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension MessageType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [MessageType] = [
|
||||
.ack,
|
||||
|
|
@ -102,11 +97,10 @@ extension MessageType: CaseIterable {
|
|||
.aht20Humidity,
|
||||
.tvocIndex,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
public struct SensorData {
|
||||
public struct SensorData: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -136,34 +130,16 @@ public struct SensorData {
|
|||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
/// The sensor data, either as a float or an uint32
|
||||
public enum OneOf_Data: Equatable {
|
||||
public enum OneOf_Data: Equatable, Sendable {
|
||||
case floatValue(Float)
|
||||
case uint32Value(UInt32)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: SensorData.OneOf_Data, rhs: SensorData.OneOf_Data) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.floatValue, .floatValue): return {
|
||||
guard case .floatValue(let l) = lhs, case .floatValue(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.uint32Value, .uint32Value): return {
|
||||
guard case .uint32Value(let l) = lhs, case .uint32Value(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
public struct InterdeviceMessage {
|
||||
public struct InterdeviceMessage: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -190,41 +166,15 @@ public struct InterdeviceMessage {
|
|||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
/// The message data
|
||||
public enum OneOf_Data: Equatable {
|
||||
public enum OneOf_Data: Equatable, Sendable {
|
||||
case nmea(String)
|
||||
case sensor(SensorData)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: InterdeviceMessage.OneOf_Data, rhs: InterdeviceMessage.OneOf_Data) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.nmea, .nmea): return {
|
||||
guard case .nmea(let l) = lhs, case .nmea(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.sensor, .sensor): return {
|
||||
guard case .sensor(let l) = lhs, case .sensor(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension MessageType: @unchecked Sendable {}
|
||||
extension SensorData: @unchecked Sendable {}
|
||||
extension SensorData.OneOf_Data: @unchecked Sendable {}
|
||||
extension InterdeviceMessage: @unchecked Sendable {}
|
||||
extension InterdeviceMessage.OneOf_Data: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/localonly.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public struct LocalConfig {
|
||||
public struct LocalConfig: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -129,7 +129,7 @@ public struct LocalConfig {
|
|||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
public struct LocalModuleConfig {
|
||||
public struct LocalModuleConfig: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -293,11 +293,6 @@ public struct LocalModuleConfig {
|
|||
fileprivate var _storage = _StorageClass.defaultInstance
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension LocalConfig: @unchecked Sendable {}
|
||||
extension LocalModuleConfig: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/module_config.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -20,7 +20,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public enum RemoteHardwarePinType: SwiftProtobuf.Enum {
|
||||
public enum RemoteHardwarePinType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -58,24 +58,18 @@ public enum RemoteHardwarePinType: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension RemoteHardwarePinType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [RemoteHardwarePinType] = [
|
||||
.unknown,
|
||||
.digitalRead,
|
||||
.digitalWrite,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
///
|
||||
/// Module Config
|
||||
public struct ModuleConfig {
|
||||
public struct ModuleConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -218,7 +212,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum OneOf_PayloadVariant: Equatable {
|
||||
public enum OneOf_PayloadVariant: Equatable, Sendable {
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case mqtt(ModuleConfig.MQTTConfig)
|
||||
|
|
@ -259,73 +253,11 @@ public struct ModuleConfig {
|
|||
/// TODO: REPLACE
|
||||
case paxcounter(ModuleConfig.PaxcounterConfig)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: ModuleConfig.OneOf_PayloadVariant, rhs: ModuleConfig.OneOf_PayloadVariant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.mqtt, .mqtt): return {
|
||||
guard case .mqtt(let l) = lhs, case .mqtt(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.serial, .serial): return {
|
||||
guard case .serial(let l) = lhs, case .serial(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.externalNotification, .externalNotification): return {
|
||||
guard case .externalNotification(let l) = lhs, case .externalNotification(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.storeForward, .storeForward): return {
|
||||
guard case .storeForward(let l) = lhs, case .storeForward(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.rangeTest, .rangeTest): return {
|
||||
guard case .rangeTest(let l) = lhs, case .rangeTest(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.telemetry, .telemetry): return {
|
||||
guard case .telemetry(let l) = lhs, case .telemetry(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.cannedMessage, .cannedMessage): return {
|
||||
guard case .cannedMessage(let l) = lhs, case .cannedMessage(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.audio, .audio): return {
|
||||
guard case .audio(let l) = lhs, case .audio(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.remoteHardware, .remoteHardware): return {
|
||||
guard case .remoteHardware(let l) = lhs, case .remoteHardware(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.neighborInfo, .neighborInfo): return {
|
||||
guard case .neighborInfo(let l) = lhs, case .neighborInfo(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.ambientLighting, .ambientLighting): return {
|
||||
guard case .ambientLighting(let l) = lhs, case .ambientLighting(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.detectionSensor, .detectionSensor): return {
|
||||
guard case .detectionSensor(let l) = lhs, case .detectionSensor(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.paxcounter, .paxcounter): return {
|
||||
guard case .paxcounter(let l) = lhs, case .paxcounter(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// MQTT Client Config
|
||||
public struct MQTTConfig {
|
||||
public struct MQTTConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -400,7 +332,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Settings for reporting unencrypted information about our node to a map via MQTT
|
||||
public struct MapReportSettings {
|
||||
public struct MapReportSettings: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -413,6 +345,10 @@ public struct ModuleConfig {
|
|||
/// Bits of precision for the location sent (default of 32 is full precision).
|
||||
public var positionPrecision: UInt32 = 0
|
||||
|
||||
///
|
||||
/// Whether we have opted-in to report our location to the map
|
||||
public var shouldReportLocation: Bool = false
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
|
|
@ -420,7 +356,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// RemoteHardwareModule Config
|
||||
public struct RemoteHardwareConfig {
|
||||
public struct RemoteHardwareConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -444,7 +380,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// NeighborInfoModule Config
|
||||
public struct NeighborInfoConfig {
|
||||
public struct NeighborInfoConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -470,7 +406,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Detection Sensor Module Config
|
||||
public struct DetectionSensorConfig {
|
||||
public struct DetectionSensorConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -517,7 +453,7 @@ public struct ModuleConfig {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum TriggerType: SwiftProtobuf.Enum {
|
||||
public enum TriggerType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
/// Event is triggered if pin is low
|
||||
|
|
@ -569,6 +505,16 @@ public struct ModuleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [
|
||||
.logicLow,
|
||||
.logicHigh,
|
||||
.fallingEdge,
|
||||
.risingEdge,
|
||||
.eitherEdgeActiveLow,
|
||||
.eitherEdgeActiveHigh,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -576,7 +522,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Audio Config for codec2 voice
|
||||
public struct AudioConfig {
|
||||
public struct AudioConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -613,7 +559,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Baudrate for codec2 voice
|
||||
public enum Audio_Baud: SwiftProtobuf.Enum {
|
||||
public enum Audio_Baud: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case codec2Default // = 0
|
||||
case codec23200 // = 1
|
||||
|
|
@ -660,6 +606,19 @@ public struct ModuleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [
|
||||
.codec2Default,
|
||||
.codec23200,
|
||||
.codec22400,
|
||||
.codec21600,
|
||||
.codec21400,
|
||||
.codec21300,
|
||||
.codec21200,
|
||||
.codec2700,
|
||||
.codec2700B,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -667,7 +626,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Config for the Paxcounter Module
|
||||
public struct PaxcounterConfig {
|
||||
public struct PaxcounterConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -693,7 +652,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Serial Config
|
||||
public struct SerialConfig {
|
||||
public struct SerialConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -736,7 +695,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum Serial_Baud: SwiftProtobuf.Enum {
|
||||
public enum Serial_Baud: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case baudDefault // = 0
|
||||
case baud110 // = 1
|
||||
|
|
@ -804,11 +763,31 @@ public struct ModuleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [
|
||||
.baudDefault,
|
||||
.baud110,
|
||||
.baud300,
|
||||
.baud600,
|
||||
.baud1200,
|
||||
.baud2400,
|
||||
.baud4800,
|
||||
.baud9600,
|
||||
.baud19200,
|
||||
.baud38400,
|
||||
.baud57600,
|
||||
.baud115200,
|
||||
.baud230400,
|
||||
.baud460800,
|
||||
.baud576000,
|
||||
.baud921600,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum Serial_Mode: SwiftProtobuf.Enum {
|
||||
public enum Serial_Mode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case `default` // = 0
|
||||
case simple // = 1
|
||||
|
|
@ -821,6 +800,10 @@ public struct ModuleConfig {
|
|||
|
||||
/// Ecowitt WS85 weather station
|
||||
case ws85 // = 6
|
||||
|
||||
/// VE.Direct is a serial protocol used by Victron Energy products
|
||||
/// https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable
|
||||
case veDirect // = 7
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -836,6 +819,7 @@ public struct ModuleConfig {
|
|||
case 4: self = .nmea
|
||||
case 5: self = .caltopo
|
||||
case 6: self = .ws85
|
||||
case 7: self = .veDirect
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -849,10 +833,23 @@ public struct ModuleConfig {
|
|||
case .nmea: return 4
|
||||
case .caltopo: return 5
|
||||
case .ws85: return 6
|
||||
case .veDirect: return 7
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [
|
||||
.default,
|
||||
.simple,
|
||||
.proto,
|
||||
.textmsg,
|
||||
.nmea,
|
||||
.caltopo,
|
||||
.ws85,
|
||||
.veDirect,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -860,7 +857,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// External Notifications Config
|
||||
public struct ExternalNotificationConfig {
|
||||
public struct ExternalNotificationConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -943,7 +940,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Store and Forward Module Config
|
||||
public struct StoreForwardConfig {
|
||||
public struct StoreForwardConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -979,7 +976,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Preferences for the RangeTestModule
|
||||
public struct RangeTestConfig {
|
||||
public struct RangeTestConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1004,7 +1001,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Configuration for both device and environment metrics
|
||||
public struct TelemetryConfig {
|
||||
public struct TelemetryConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1073,7 +1070,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// Canned Messages Module Config
|
||||
public struct CannedMessageConfig {
|
||||
public struct CannedMessageConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1128,7 +1125,7 @@ public struct ModuleConfig {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum InputEventChar: SwiftProtobuf.Enum {
|
||||
public enum InputEventChar: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -1196,6 +1193,18 @@ public struct ModuleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [
|
||||
.none,
|
||||
.up,
|
||||
.down,
|
||||
.left,
|
||||
.right,
|
||||
.select,
|
||||
.back,
|
||||
.cancel,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -1204,7 +1213,7 @@ public struct ModuleConfig {
|
|||
///
|
||||
///Ambient Lighting Module - Settings for control of onboard LEDs to allow users to adjust the brightness levels and respective color levels.
|
||||
///Initially created for the RAK14001 RGB LED module.
|
||||
public struct AmbientLightingConfig {
|
||||
public struct AmbientLightingConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1237,89 +1246,9 @@ public struct ModuleConfig {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension ModuleConfig.DetectionSensorConfig.TriggerType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.DetectionSensorConfig.TriggerType] = [
|
||||
.logicLow,
|
||||
.logicHigh,
|
||||
.fallingEdge,
|
||||
.risingEdge,
|
||||
.eitherEdgeActiveLow,
|
||||
.eitherEdgeActiveHigh,
|
||||
]
|
||||
}
|
||||
|
||||
extension ModuleConfig.AudioConfig.Audio_Baud: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.AudioConfig.Audio_Baud] = [
|
||||
.codec2Default,
|
||||
.codec23200,
|
||||
.codec22400,
|
||||
.codec21600,
|
||||
.codec21400,
|
||||
.codec21300,
|
||||
.codec21200,
|
||||
.codec2700,
|
||||
.codec2700B,
|
||||
]
|
||||
}
|
||||
|
||||
extension ModuleConfig.SerialConfig.Serial_Baud: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.SerialConfig.Serial_Baud] = [
|
||||
.baudDefault,
|
||||
.baud110,
|
||||
.baud300,
|
||||
.baud600,
|
||||
.baud1200,
|
||||
.baud2400,
|
||||
.baud4800,
|
||||
.baud9600,
|
||||
.baud19200,
|
||||
.baud38400,
|
||||
.baud57600,
|
||||
.baud115200,
|
||||
.baud230400,
|
||||
.baud460800,
|
||||
.baud576000,
|
||||
.baud921600,
|
||||
]
|
||||
}
|
||||
|
||||
extension ModuleConfig.SerialConfig.Serial_Mode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.SerialConfig.Serial_Mode] = [
|
||||
.default,
|
||||
.simple,
|
||||
.proto,
|
||||
.textmsg,
|
||||
.nmea,
|
||||
.caltopo,
|
||||
.ws85,
|
||||
]
|
||||
}
|
||||
|
||||
extension ModuleConfig.CannedMessageConfig.InputEventChar: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [ModuleConfig.CannedMessageConfig.InputEventChar] = [
|
||||
.none,
|
||||
.up,
|
||||
.down,
|
||||
.left,
|
||||
.right,
|
||||
.select,
|
||||
.back,
|
||||
.cancel,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// A GPIO pin definition for remote hardware module
|
||||
public struct RemoteHardwarePin {
|
||||
public struct RemoteHardwarePin: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1341,32 +1270,6 @@ public struct RemoteHardwarePin {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension RemoteHardwarePinType: @unchecked Sendable {}
|
||||
extension ModuleConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.OneOf_PayloadVariant: @unchecked Sendable {}
|
||||
extension ModuleConfig.MQTTConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.MapReportSettings: @unchecked Sendable {}
|
||||
extension ModuleConfig.RemoteHardwareConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.NeighborInfoConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.DetectionSensorConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.DetectionSensorConfig.TriggerType: @unchecked Sendable {}
|
||||
extension ModuleConfig.AudioConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.AudioConfig.Audio_Baud: @unchecked Sendable {}
|
||||
extension ModuleConfig.PaxcounterConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.SerialConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.SerialConfig.Serial_Baud: @unchecked Sendable {}
|
||||
extension ModuleConfig.SerialConfig.Serial_Mode: @unchecked Sendable {}
|
||||
extension ModuleConfig.ExternalNotificationConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.StoreForwardConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.RangeTestConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.TelemetryConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.CannedMessageConfig: @unchecked Sendable {}
|
||||
extension ModuleConfig.CannedMessageConfig.InputEventChar: @unchecked Sendable {}
|
||||
extension ModuleConfig.AmbientLightingConfig: @unchecked Sendable {}
|
||||
extension RemoteHardwarePin: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -1748,6 +1651,7 @@ extension ModuleConfig.MapReportSettings: SwiftProtobuf.Message, SwiftProtobuf._
|
|||
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
|
||||
1: .standard(proto: "publish_interval_secs"),
|
||||
2: .standard(proto: "position_precision"),
|
||||
3: .standard(proto: "should_report_location"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -1758,6 +1662,7 @@ extension ModuleConfig.MapReportSettings: SwiftProtobuf.Message, SwiftProtobuf._
|
|||
switch fieldNumber {
|
||||
case 1: try { try decoder.decodeSingularUInt32Field(value: &self.publishIntervalSecs) }()
|
||||
case 2: try { try decoder.decodeSingularUInt32Field(value: &self.positionPrecision) }()
|
||||
case 3: try { try decoder.decodeSingularBoolField(value: &self.shouldReportLocation) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -1770,12 +1675,16 @@ extension ModuleConfig.MapReportSettings: SwiftProtobuf.Message, SwiftProtobuf._
|
|||
if self.positionPrecision != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.positionPrecision, fieldNumber: 2)
|
||||
}
|
||||
if self.shouldReportLocation != false {
|
||||
try visitor.visitSingularBoolField(value: self.shouldReportLocation, fieldNumber: 3)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
public static func ==(lhs: ModuleConfig.MapReportSettings, rhs: ModuleConfig.MapReportSettings) -> Bool {
|
||||
if lhs.publishIntervalSecs != rhs.publishIntervalSecs {return false}
|
||||
if lhs.positionPrecision != rhs.positionPrecision {return false}
|
||||
if lhs.shouldReportLocation != rhs.shouldReportLocation {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
@ -2190,6 +2099,7 @@ extension ModuleConfig.SerialConfig.Serial_Mode: SwiftProtobuf._ProtoNameProvidi
|
|||
4: .same(proto: "NMEA"),
|
||||
5: .same(proto: "CALTOPO"),
|
||||
6: .same(proto: "WS85"),
|
||||
7: .same(proto: "VE_DIRECT"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/mqtt.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// This message wraps a MeshPacket with extra metadata about the sender and how it arrived.
|
||||
public struct ServiceEnvelope {
|
||||
public struct ServiceEnvelope: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -57,7 +57,7 @@ public struct ServiceEnvelope {
|
|||
|
||||
///
|
||||
/// Information about a node intended to be reported unencrypted to a map using MQTT.
|
||||
public struct MapReport {
|
||||
public struct MapReport: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -116,16 +116,16 @@ public struct MapReport {
|
|||
/// Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT)
|
||||
public var numOnlineLocalNodes: UInt32 = 0
|
||||
|
||||
///
|
||||
/// User has opted in to share their location (map report) with the mqtt server
|
||||
/// Controlled by map_report.should_report_location
|
||||
public var hasOptedReportLocation_p: Bool = false
|
||||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension ServiceEnvelope: @unchecked Sendable {}
|
||||
extension MapReport: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -194,6 +194,7 @@ extension MapReport: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
11: .same(proto: "altitude"),
|
||||
12: .standard(proto: "position_precision"),
|
||||
13: .standard(proto: "num_online_local_nodes"),
|
||||
14: .standard(proto: "has_opted_report_location"),
|
||||
]
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
|
|
@ -215,6 +216,7 @@ extension MapReport: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
case 11: try { try decoder.decodeSingularInt32Field(value: &self.altitude) }()
|
||||
case 12: try { try decoder.decodeSingularUInt32Field(value: &self.positionPrecision) }()
|
||||
case 13: try { try decoder.decodeSingularUInt32Field(value: &self.numOnlineLocalNodes) }()
|
||||
case 14: try { try decoder.decodeSingularBoolField(value: &self.hasOptedReportLocation_p) }()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
|
@ -260,6 +262,9 @@ extension MapReport: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
if self.numOnlineLocalNodes != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.numOnlineLocalNodes, fieldNumber: 13)
|
||||
}
|
||||
if self.hasOptedReportLocation_p != false {
|
||||
try visitor.visitSingularBoolField(value: self.hasOptedReportLocation_p, fieldNumber: 14)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
|
|
@ -277,6 +282,7 @@ extension MapReport: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementation
|
|||
if lhs.altitude != rhs.altitude {return false}
|
||||
if lhs.positionPrecision != rhs.positionPrecision {return false}
|
||||
if lhs.numOnlineLocalNodes != rhs.numOnlineLocalNodes {return false}
|
||||
if lhs.hasOptedReportLocation_p != rhs.hasOptedReportLocation_p {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/paxcount.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public struct Paxcount {
|
||||
public struct Paxcount: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -44,10 +44,6 @@ public struct Paxcount {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension Paxcount: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/portnums.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -33,7 +33,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
/// Note: This was formerly a Type enum named 'typ' with the same id #
|
||||
/// We have change to this 'portnum' based scheme for specifying app handlers for particular payloads.
|
||||
/// This change is backwards compatible by treating the legacy OPAQUE/CLEAR_TEXT values identically.
|
||||
public enum PortNum: SwiftProtobuf.Enum {
|
||||
public enum PortNum: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -290,11 +290,6 @@ public enum PortNum: SwiftProtobuf.Enum {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension PortNum: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [PortNum] = [
|
||||
.unknownApp,
|
||||
|
|
@ -328,14 +323,9 @@ extension PortNum: CaseIterable {
|
|||
.atakForwarder,
|
||||
.max,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension PortNum: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
extension PortNum: SwiftProtobuf._ProtoNameProviding {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/powermon.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
/// Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
|
||||
///But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us)
|
||||
public struct PowerMon {
|
||||
public struct PowerMon: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -31,7 +31,7 @@ public struct PowerMon {
|
|||
|
||||
/// Any significant power changing event in meshtastic should be tagged with a powermon state transition.
|
||||
///If you are making new meshtastic features feel free to add new entries at the end of this definition.
|
||||
public enum State: SwiftProtobuf.Enum {
|
||||
public enum State: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case none // = 0
|
||||
case cpuDeepSleep // = 1
|
||||
|
|
@ -104,37 +104,31 @@ public struct PowerMon {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [PowerMon.State] = [
|
||||
.none,
|
||||
.cpuDeepSleep,
|
||||
.cpuLightSleep,
|
||||
.vext1On,
|
||||
.loraRxon,
|
||||
.loraTxon,
|
||||
.loraRxactive,
|
||||
.btOn,
|
||||
.ledOn,
|
||||
.screenOn,
|
||||
.screenDrawing,
|
||||
.wifiOn,
|
||||
.gpsActive,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension PowerMon.State: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [PowerMon.State] = [
|
||||
.none,
|
||||
.cpuDeepSleep,
|
||||
.cpuLightSleep,
|
||||
.vext1On,
|
||||
.loraRxon,
|
||||
.loraTxon,
|
||||
.loraRxactive,
|
||||
.btOn,
|
||||
.ledOn,
|
||||
.screenOn,
|
||||
.screenDrawing,
|
||||
.wifiOn,
|
||||
.gpsActive,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
///
|
||||
/// PowerStress testing support via the C++ PowerStress module
|
||||
public struct PowerStressMessage {
|
||||
public struct PowerStressMessage: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -151,7 +145,7 @@ public struct PowerStressMessage {
|
|||
/// What operation would we like the UUT to perform.
|
||||
///note: senders should probably set want_response in their request packets, so that they can know when the state
|
||||
///machine has started processing their request
|
||||
public enum Opcode: SwiftProtobuf.Enum {
|
||||
public enum Opcode: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -272,48 +266,35 @@ public struct PowerStressMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [PowerStressMessage.Opcode] = [
|
||||
.unset,
|
||||
.printInfo,
|
||||
.forceQuiet,
|
||||
.endQuiet,
|
||||
.screenOn,
|
||||
.screenOff,
|
||||
.cpuIdle,
|
||||
.cpuDeepsleep,
|
||||
.cpuFullon,
|
||||
.ledOn,
|
||||
.ledOff,
|
||||
.loraOff,
|
||||
.loraTx,
|
||||
.loraRx,
|
||||
.btOff,
|
||||
.btOn,
|
||||
.wifiOff,
|
||||
.wifiOn,
|
||||
.gpsOff,
|
||||
.gpsOn,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension PowerStressMessage.Opcode: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [PowerStressMessage.Opcode] = [
|
||||
.unset,
|
||||
.printInfo,
|
||||
.forceQuiet,
|
||||
.endQuiet,
|
||||
.screenOn,
|
||||
.screenOff,
|
||||
.cpuIdle,
|
||||
.cpuDeepsleep,
|
||||
.cpuFullon,
|
||||
.ledOn,
|
||||
.ledOff,
|
||||
.loraOff,
|
||||
.loraTx,
|
||||
.loraRx,
|
||||
.btOff,
|
||||
.btOn,
|
||||
.wifiOff,
|
||||
.wifiOn,
|
||||
.gpsOff,
|
||||
.gpsOn,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension PowerMon: @unchecked Sendable {}
|
||||
extension PowerMon.State: @unchecked Sendable {}
|
||||
extension PowerStressMessage: @unchecked Sendable {}
|
||||
extension PowerStressMessage.Opcode: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -323,8 +304,8 @@ extension PowerMon: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationB
|
|||
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
|
||||
|
||||
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
|
||||
while let _ = try decoder.nextFieldNumber() {
|
||||
}
|
||||
// Load everything into unknown fields
|
||||
while try decoder.nextFieldNumber() != nil {}
|
||||
}
|
||||
|
||||
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
|
|
@ -379,7 +360,7 @@ extension PowerStressMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
|
|||
if self.cmd != .unset {
|
||||
try visitor.visitSingularEnumField(value: self.cmd, fieldNumber: 1)
|
||||
}
|
||||
if self.numSeconds != 0 {
|
||||
if self.numSeconds.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.numSeconds, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/remote_hardware.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -30,7 +30,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
/// because no security yet (beyond the channel mechanism).
|
||||
/// It should be off by default and then protected based on some TBD mechanism
|
||||
/// (a special channel once multichannel support is included?)
|
||||
public struct HardwareMessage {
|
||||
public struct HardwareMessage: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -52,7 +52,7 @@ public struct HardwareMessage {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum TypeEnum: SwiftProtobuf.Enum {
|
||||
public enum TypeEnum: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -110,32 +110,21 @@ public struct HardwareMessage {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [HardwareMessage.TypeEnum] = [
|
||||
.unset,
|
||||
.writeGpios,
|
||||
.watchGpios,
|
||||
.gpiosChanged,
|
||||
.readGpios,
|
||||
.readGpiosReply,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension HardwareMessage.TypeEnum: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [HardwareMessage.TypeEnum] = [
|
||||
.unset,
|
||||
.writeGpios,
|
||||
.watchGpios,
|
||||
.gpiosChanged,
|
||||
.readGpios,
|
||||
.readGpiosReply,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension HardwareMessage: @unchecked Sendable {}
|
||||
extension HardwareMessage.TypeEnum: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/rtttl.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// Canned message module configuration.
|
||||
public struct RTTTLConfig {
|
||||
public struct RTTTLConfig: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -36,10 +36,6 @@ public struct RTTTLConfig {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension RTTTLConfig: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/storeforward.proto
|
||||
|
|
@ -22,7 +23,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public struct StoreAndForward {
|
||||
public struct StoreAndForward: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -79,7 +80,7 @@ public struct StoreAndForward {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public enum OneOf_Variant: Equatable {
|
||||
public enum OneOf_Variant: Equatable, @unchecked Sendable {
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
case stats(StoreAndForward.Statistics)
|
||||
|
|
@ -93,38 +94,12 @@ public struct StoreAndForward {
|
|||
/// Text from history message.
|
||||
case text(Data)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: StoreAndForward.OneOf_Variant, rhs: StoreAndForward.OneOf_Variant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.stats, .stats): return {
|
||||
guard case .stats(let l) = lhs, case .stats(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.history, .history): return {
|
||||
guard case .history(let l) = lhs, case .history(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.heartbeat, .heartbeat): return {
|
||||
guard case .heartbeat(let l) = lhs, case .heartbeat(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.text, .text): return {
|
||||
guard case .text(let l) = lhs, case .text(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// 001 - 063 = From Router
|
||||
/// 064 - 127 = From Client
|
||||
public enum RequestResponse: SwiftProtobuf.Enum {
|
||||
public enum RequestResponse: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -242,11 +217,31 @@ public struct StoreAndForward {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [StoreAndForward.RequestResponse] = [
|
||||
.unset,
|
||||
.routerError,
|
||||
.routerHeartbeat,
|
||||
.routerPing,
|
||||
.routerPong,
|
||||
.routerBusy,
|
||||
.routerHistory,
|
||||
.routerStats,
|
||||
.routerTextDirect,
|
||||
.routerTextBroadcast,
|
||||
.clientError,
|
||||
.clientHistory,
|
||||
.clientStats,
|
||||
.clientPing,
|
||||
.clientPong,
|
||||
.clientAbort,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public struct Statistics {
|
||||
public struct Statistics: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -294,7 +289,7 @@ public struct StoreAndForward {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public struct History {
|
||||
public struct History: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -319,7 +314,7 @@ public struct StoreAndForward {
|
|||
|
||||
///
|
||||
/// TODO: REPLACE
|
||||
public struct Heartbeat {
|
||||
public struct Heartbeat: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -340,41 +335,6 @@ public struct StoreAndForward {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension StoreAndForward.RequestResponse: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [StoreAndForward.RequestResponse] = [
|
||||
.unset,
|
||||
.routerError,
|
||||
.routerHeartbeat,
|
||||
.routerPing,
|
||||
.routerPong,
|
||||
.routerBusy,
|
||||
.routerHistory,
|
||||
.routerStats,
|
||||
.routerTextDirect,
|
||||
.routerTextBroadcast,
|
||||
.clientError,
|
||||
.clientHistory,
|
||||
.clientStats,
|
||||
.clientPing,
|
||||
.clientPong,
|
||||
.clientAbort,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension StoreAndForward: @unchecked Sendable {}
|
||||
extension StoreAndForward.OneOf_Variant: @unchecked Sendable {}
|
||||
extension StoreAndForward.RequestResponse: @unchecked Sendable {}
|
||||
extension StoreAndForward.Statistics: @unchecked Sendable {}
|
||||
extension StoreAndForward.History: @unchecked Sendable {}
|
||||
extension StoreAndForward.Heartbeat: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/telemetry.proto
|
||||
|
|
@ -7,7 +8,6 @@
|
|||
// For information on using the generated types, please see the documentation:
|
||||
// https://github.com/apple/swift-protobuf/
|
||||
|
||||
import Foundation
|
||||
import SwiftProtobuf
|
||||
|
||||
// If the compiler emits an error on this type, it is because this file
|
||||
|
|
@ -22,7 +22,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
|
||||
///
|
||||
/// Supported I2C Sensors for telemetry in Meshtastic
|
||||
public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
||||
public enum TelemetrySensorType: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
|
||||
///
|
||||
|
|
@ -176,6 +176,10 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
///
|
||||
/// RAKWireless RAK12035 Soil Moisture Sensor Module
|
||||
case rak12035 // = 37
|
||||
|
||||
///
|
||||
/// MAX17261 lipo battery gauge
|
||||
case max17261 // = 38
|
||||
case UNRECOGNIZED(Int)
|
||||
|
||||
public init() {
|
||||
|
|
@ -222,6 +226,7 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case 35: self = .dfrobotRain
|
||||
case 36: self = .dps310
|
||||
case 37: self = .rak12035
|
||||
case 38: self = .max17261
|
||||
default: self = .UNRECOGNIZED(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -266,15 +271,11 @@ public enum TelemetrySensorType: SwiftProtobuf.Enum {
|
|||
case .dfrobotRain: return 35
|
||||
case .dps310: return 36
|
||||
case .rak12035: return 37
|
||||
case .max17261: return 38
|
||||
case .UNRECOGNIZED(let i): return i
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension TelemetrySensorType: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [TelemetrySensorType] = [
|
||||
.sensorUnset,
|
||||
|
|
@ -315,14 +316,14 @@ extension TelemetrySensorType: CaseIterable {
|
|||
.dfrobotRain,
|
||||
.dps310,
|
||||
.rak12035,
|
||||
.max17261,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
}
|
||||
|
||||
///
|
||||
/// Key native device metrics such as battery level
|
||||
public struct DeviceMetrics {
|
||||
public struct DeviceMetrics: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -395,7 +396,7 @@ public struct DeviceMetrics {
|
|||
|
||||
///
|
||||
/// Weather station or other environmental metrics
|
||||
public struct EnvironmentMetrics {
|
||||
public struct EnvironmentMetrics: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -653,7 +654,7 @@ public struct EnvironmentMetrics {
|
|||
|
||||
///
|
||||
/// Power Metrics (voltage / current / etc)
|
||||
public struct PowerMetrics {
|
||||
public struct PowerMetrics: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -738,7 +739,7 @@ public struct PowerMetrics {
|
|||
|
||||
///
|
||||
/// Air quality metrics
|
||||
public struct AirQualityMetrics {
|
||||
public struct AirQualityMetrics: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -907,7 +908,7 @@ public struct AirQualityMetrics {
|
|||
|
||||
///
|
||||
/// Local device mesh statistics
|
||||
public struct LocalStats {
|
||||
public struct LocalStats: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -965,7 +966,7 @@ public struct LocalStats {
|
|||
|
||||
///
|
||||
/// Health telemetry metrics
|
||||
public struct HealthMetrics {
|
||||
public struct HealthMetrics: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1014,7 +1015,7 @@ public struct HealthMetrics {
|
|||
|
||||
///
|
||||
/// Types of Measurements the telemetry module is equipped to handle
|
||||
public struct Telemetry {
|
||||
public struct Telemetry: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1087,7 +1088,7 @@ public struct Telemetry {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum OneOf_Variant: Equatable {
|
||||
public enum OneOf_Variant: Equatable, Sendable {
|
||||
///
|
||||
/// Key native device metrics such as battery level
|
||||
case deviceMetrics(DeviceMetrics)
|
||||
|
|
@ -1107,40 +1108,6 @@ public struct Telemetry {
|
|||
/// Health telemetry metrics
|
||||
case healthMetrics(HealthMetrics)
|
||||
|
||||
#if !swift(>=4.1)
|
||||
public static func ==(lhs: Telemetry.OneOf_Variant, rhs: Telemetry.OneOf_Variant) -> Bool {
|
||||
// The use of inline closures is to circumvent an issue where the compiler
|
||||
// allocates stack space for every case branch when no optimizations are
|
||||
// enabled. https://github.com/apple/swift-protobuf/issues/1034
|
||||
switch (lhs, rhs) {
|
||||
case (.deviceMetrics, .deviceMetrics): return {
|
||||
guard case .deviceMetrics(let l) = lhs, case .deviceMetrics(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.environmentMetrics, .environmentMetrics): return {
|
||||
guard case .environmentMetrics(let l) = lhs, case .environmentMetrics(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.airQualityMetrics, .airQualityMetrics): return {
|
||||
guard case .airQualityMetrics(let l) = lhs, case .airQualityMetrics(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.powerMetrics, .powerMetrics): return {
|
||||
guard case .powerMetrics(let l) = lhs, case .powerMetrics(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.localStats, .localStats): return {
|
||||
guard case .localStats(let l) = lhs, case .localStats(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
case (.healthMetrics, .healthMetrics): return {
|
||||
guard case .healthMetrics(let l) = lhs, case .healthMetrics(let r) = rhs else { preconditionFailure() }
|
||||
return l == r
|
||||
}()
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public init() {}
|
||||
|
|
@ -1148,7 +1115,7 @@ public struct Telemetry {
|
|||
|
||||
///
|
||||
/// NAU7802 Telemetry configuration, for saving to flash
|
||||
public struct Nau7802Config {
|
||||
public struct Nau7802Config: Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -1166,19 +1133,6 @@ public struct Nau7802Config {
|
|||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension TelemetrySensorType: @unchecked Sendable {}
|
||||
extension DeviceMetrics: @unchecked Sendable {}
|
||||
extension EnvironmentMetrics: @unchecked Sendable {}
|
||||
extension PowerMetrics: @unchecked Sendable {}
|
||||
extension AirQualityMetrics: @unchecked Sendable {}
|
||||
extension LocalStats: @unchecked Sendable {}
|
||||
extension HealthMetrics: @unchecked Sendable {}
|
||||
extension Telemetry: @unchecked Sendable {}
|
||||
extension Telemetry.OneOf_Variant: @unchecked Sendable {}
|
||||
extension Nau7802Config: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
@ -1223,6 +1177,7 @@ extension TelemetrySensorType: SwiftProtobuf._ProtoNameProviding {
|
|||
35: .same(proto: "DFROBOT_RAIN"),
|
||||
36: .same(proto: "DPS310"),
|
||||
37: .same(proto: "RAK12035"),
|
||||
38: .same(proto: "MAX17261"),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1746,10 +1701,10 @@ extension LocalStats: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementatio
|
|||
if self.uptimeSeconds != 0 {
|
||||
try visitor.visitSingularUInt32Field(value: self.uptimeSeconds, fieldNumber: 1)
|
||||
}
|
||||
if self.channelUtilization != 0 {
|
||||
if self.channelUtilization.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.channelUtilization, fieldNumber: 2)
|
||||
}
|
||||
if self.airUtilTx != 0 {
|
||||
if self.airUtilTx.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.airUtilTx, fieldNumber: 3)
|
||||
}
|
||||
if self.numPacketsTx != 0 {
|
||||
|
|
@ -2016,7 +1971,7 @@ extension Nau7802Config: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementa
|
|||
if self.zeroOffset != 0 {
|
||||
try visitor.visitSingularInt32Field(value: self.zeroOffset, fieldNumber: 1)
|
||||
}
|
||||
if self.calibrationFactor != 0 {
|
||||
if self.calibrationFactor.bitPattern != 0 {
|
||||
try visitor.visitSingularFloatField(value: self.calibrationFactor, fieldNumber: 2)
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// DO NOT EDIT.
|
||||
// swift-format-ignore-file
|
||||
// swiftlint:disable all
|
||||
//
|
||||
// Generated by the Swift generator plugin for the protocol buffer compiler.
|
||||
// Source: meshtastic/xmodem.proto
|
||||
|
|
@ -20,7 +21,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP
|
|||
typealias Version = _2
|
||||
}
|
||||
|
||||
public struct XModem {
|
||||
public struct XModem: @unchecked Sendable {
|
||||
// SwiftProtobuf.Message conformance is added in an extension below. See the
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
|
@ -35,7 +36,7 @@ public struct XModem {
|
|||
|
||||
public var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
public enum Control: SwiftProtobuf.Enum {
|
||||
public enum Control: SwiftProtobuf.Enum, Swift.CaseIterable {
|
||||
public typealias RawValue = Int
|
||||
case nul // = 0
|
||||
case soh // = 1
|
||||
|
|
@ -79,34 +80,23 @@ public struct XModem {
|
|||
}
|
||||
}
|
||||
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [XModem.Control] = [
|
||||
.nul,
|
||||
.soh,
|
||||
.stx,
|
||||
.eot,
|
||||
.ack,
|
||||
.nak,
|
||||
.can,
|
||||
.ctrlz,
|
||||
]
|
||||
|
||||
}
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
#if swift(>=4.2)
|
||||
|
||||
extension XModem.Control: CaseIterable {
|
||||
// The compiler won't synthesize support with the UNRECOGNIZED case.
|
||||
public static let allCases: [XModem.Control] = [
|
||||
.nul,
|
||||
.soh,
|
||||
.stx,
|
||||
.eot,
|
||||
.ack,
|
||||
.nak,
|
||||
.can,
|
||||
.ctrlz,
|
||||
]
|
||||
}
|
||||
|
||||
#endif // swift(>=4.2)
|
||||
|
||||
#if swift(>=5.5) && canImport(_Concurrency)
|
||||
extension XModem: @unchecked Sendable {}
|
||||
extension XModem.Control: @unchecked Sendable {}
|
||||
#endif // swift(>=5.5) && canImport(_Concurrency)
|
||||
|
||||
// MARK: - Code below here is support for the SwiftProtobuf runtime.
|
||||
|
||||
fileprivate let _protobuf_package = "meshtastic"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 068646653e8375fc145988026ad242a3cf70f7ab
|
||||
Subproject commit 816595c8bbdfc3b4388e11348ccd043294d58705
|
||||
Loading…
Add table
Add a link
Reference in a new issue