├── src ├── DeleteOldDownloads.sh ├── CleanProfiles.sh ├── BuildLoopDev.sh ├── BuildLoopReleased.sh ├── CleanDerived.sh ├── XcodeClean.sh ├── BuildLoopCaregiver.sh ├── BuildxDrip4iOS.sh ├── TrioBuildSelectScript.sh ├── BuildLoopFollow.sh ├── BuildSelectScript.sh ├── BuildLoop.sh ├── BuildTrio.sh ├── CustomizationSelect.sh └── Build_iAPS.sh ├── CleanProfCartDerived.sh ├── clear_custom_config.sh ├── BuildLoopDev.sh ├── BuildLoopReleased.sh ├── inline_functions ├── clean_profiles.sh ├── run_script.sh ├── before_final_return_message.sh ├── utility_scripts.sh ├── build_warning.sh ├── building_verify_version.sh ├── loopfollow_functions.sh ├── common.sh ├── delete_old_downloads.sh ├── building_config_override.sh ├── build_functions.sh └── patch_functions.sh ├── patch ├── g6g7_upload_readings.patch ├── cage.patch └── g6g7_sage.patch ├── patch_cto ├── cto_main_LoopKit.patch └── add_ab_ramp_option_LoopWorkspace_dev_0493004.patch ├── custom_config.sh ├── CleanDerived.sh ├── XcodeClean.sh ├── CleanProfiles.sh ├── README.md ├── DeleteOldDownloads.sh ├── TrioBuildSelectScript.sh └── BuildSelectScript.sh /src/DeleteOldDownloads.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #!inline common.sh 4 | 5 | #!inline delete_old_downloads.sh 6 | 7 | delete_old_downloads 8 | 9 | -------------------------------------------------------------------------------- /src/CleanProfiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #!inline common.sh 4 | 5 | #!inline clean_profiles.sh 6 | 7 | section_separator 8 | clean_profiles 9 | exit_script 10 | -------------------------------------------------------------------------------- /src/BuildLoopDev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopDev.sh 2 | 3 | script_name="BuildLoopDev.sh" 4 | 5 | # add special font 6 | NC='\033[0m' 7 | ERROR_FONT='\033[1;31m' 8 | 9 | # redirect message 10 | echo -e "${ERROR_FONT}The ${script_name} script has been superceded:${NC}" 11 | echo " Use the BuildLoop script instead:" 12 | echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)\"" 13 | -------------------------------------------------------------------------------- /src/BuildLoopReleased.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopReleased.sh 2 | 3 | script_name="BuildLoopReleased.sh" 4 | 5 | # add special font 6 | NC='\033[0m' 7 | ERROR_FONT='\033[1;31m' 8 | 9 | # redirect message 10 | echo -e "${ERROR_FONT}The ${script_name} script has been superceded:${NC}" 11 | echo " Use the BuildLoop script instead:" 12 | echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)\"" 13 | -------------------------------------------------------------------------------- /CleanProfCartDerived.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clear 4 | echo -e "\n\n🕒 Please be patient. On older computers and virtual machines, this may take 5-10 minutes or longer to run.\n" 5 | echo -e "\n\n✅ Cleaning Profiles.\n" 6 | rm -rf ~/Library/MobileDevice/Provisioning\ Profiles 7 | echo -e "✅ Cleaning Derived Data files.\n" 8 | rm -rf ~/Library/Developer/Xcode/DerivedData 9 | echo -e "✅ Done Cleaning.\n\n⚠️ If Xcode is open, Quit it and open it again.\n" 10 | echo -e "⬆️ You can press the up arrow on the keyboard followed by the Enter key to start the script from the beginning.\n\n"; 11 | exit 0 12 | -------------------------------------------------------------------------------- /src/CleanDerived.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #!inline common.sh 4 | 5 | section_separator 6 | echo -e "${INFO_FONT}If you did not quit Xcode before selecting, you might see errors${NC}" 7 | echo -e "\n\n🕒 Please be patient. On older computers and virtual machines, this may take 5-10 minutes or longer to run.\n" 8 | echo -e "✅ Cleaning Derived Data files.\n" 9 | rm -rf ~/Library/Developer/Xcode/DerivedData 10 | echo -e "✅ Done Cleaning" 11 | echo -e " If Xcode was open, you may see a 'Permission denied' statement." 12 | echo -e " In that case, quit out of Xcode and run the script again\n" 13 | exit_script 14 | -------------------------------------------------------------------------------- /clear_custom_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Unset environment variables related to testing 4 | # This script MUST be run using source in order to affect the build scripts. 5 | # For example: source clear_custom_config.sh&&./BuildLoop.sh 6 | 7 | unset SCRIPT_BRANCH 8 | unset LOCAL_SCRIPT 9 | unset FRESH_CLONE 10 | unset CLONE_STATUS 11 | unset SKIP_OPEN_SOURCE_WARNING 12 | unset CUSTOM_URL 13 | unset CUSTOM_BRANCH 14 | unset CUSTOM_MACOS_VER 15 | unset CUSTOM_XCODE_VER 16 | unset DELETE_SELECTED_FOLDERS 17 | unset PATCH_BRANCH 18 | unset PATCH_REPO 19 | unset LOCAL_PATCH_FOLDER 20 | unset CUSTOMIZATION_DEBUG 21 | -------------------------------------------------------------------------------- /BuildLoopDev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopDev.sh 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | script_name="BuildLoopDev.sh" 9 | 10 | # add special font 11 | NC='\033[0m' 12 | ERROR_FONT='\033[1;31m' 13 | 14 | # redirect message 15 | echo -e "${ERROR_FONT}The ${script_name} script has been superceded:${NC}" 16 | echo " Use the BuildLoop script instead:" 17 | echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)\"" 18 | # *** End of inlined file: src/BuildLoopDev.sh *** 19 | 20 | -------------------------------------------------------------------------------- /BuildLoopReleased.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopReleased.sh 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | script_name="BuildLoopReleased.sh" 9 | 10 | # add special font 11 | NC='\033[0m' 12 | ERROR_FONT='\033[1;31m' 13 | 14 | # redirect message 15 | echo -e "${ERROR_FONT}The ${script_name} script has been superceded:${NC}" 16 | echo " Use the BuildLoop script instead:" 17 | echo "/bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)\"" 18 | # *** End of inlined file: src/BuildLoopReleased.sh *** 19 | 20 | -------------------------------------------------------------------------------- /src/XcodeClean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #!inline common.sh 4 | 5 | section_separator 6 | echo -e "${INFO_FONT}If you did not quit Xcode before selecting, this might not clean everything${NC}" 7 | 8 | echo -e "\n\n🕒 Please be patient. On older computers and virtual machines, this may take 5-10 minutes or longer to run.\n" 9 | 10 | echo -e "\n\n✅ Removing Developer iOS DeviceSupport Library\n" 11 | rm -rf "$HOME/Library/Developer/Xcode/iOS\ DeviceSupport" 12 | 13 | echo -e "✅ Removing Developer watchOS DeviceSupport Library\n" 14 | rm -rf "$HOME/Library/Developer/Xcode/watchOS\ DeviceSupport" 15 | 16 | echo -e "✅ Removing Developer DerivedData\n" 17 | rm -rf "$HOME/Library/Developer/Xcode/DerivedData" 18 | 19 | echo -e " If Xcode was open, you may see a 'Permission denied' statement." 20 | echo -e " In that case, quit out of Xcode and run the script again before rebooting\n" 21 | 22 | echo -e "🛑 Please Reboot Now\n\n"; 23 | exit_script 24 | -------------------------------------------------------------------------------- /inline_functions/clean_profiles.sh: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # clean_profiles function 3 | # Action: deletes saved mobileprovisions from Mac 4 | # Information: 5 | # If Xcode is open, *.mobileprovisions are deleted and new ones generated 6 | # The path changed between Xcode 15 and Xcode 16, delete both folders 7 | ############################################################ 8 | 9 | clean_profiles() { 10 | xcode15_path=${HOME}/Library/MobileDevice/Provisioning\ Profiles 11 | xcode16_path=${HOME}/Library/Developer/Xcode/UserData/Provisioning\ Profiles 12 | 13 | echo -e "\n✅ Cleaning Profiles" 14 | echo -e " This ensures the next app you build with Xcode will last a year." 15 | if [[ -d "$xcode15_path" ]]; then 16 | rm -rf "$xcode15_path" 17 | fi 18 | if [[ -d "$xcode16_path" ]]; then 19 | rm -rf "$xcode16_path" 20 | fi 21 | echo -e "✅ Profiles are cleaned." 22 | } 23 | -------------------------------------------------------------------------------- /inline_functions/run_script.sh: -------------------------------------------------------------------------------- 1 | # The function fetches and executes a script either from LnL GitHub repository 2 | # or from the current local directory (if LOCAL_SCRIPT is set to "1"). 3 | # The script is executed with "_" as parameter $0, telling the script that it is 4 | # run from within the ecosystem of LnL. 5 | # run_script accepts two parameters: 6 | # 1. script_name: The name of the script to be executed. 7 | # 2. extra_arg (optional): An additional argument to be passed to the script. 8 | # If the script fails to execute, the function prints an error message and terminates 9 | # the entire shell script with a non-zero status code. 10 | run_script() { 11 | local script_name=$1 12 | local extra_arg=$2 13 | echo -e "\n--------------------------------\n" 14 | echo -e "Executing Script: $script_name" 15 | echo -e "\n--------------------------------\n" 16 | 17 | if [[ ${LOCAL_SCRIPT:-0} -eq 0 ]]; then 18 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/$SCRIPT_BRANCH/$script_name)" _ "$extra_arg" 19 | else 20 | /bin/bash -c "$(cat $script_name)" _ "$extra_arg" 21 | fi 22 | 23 | if [ $? -ne 0 ]; then 24 | echo "Error: Failed to execute $script_name" 25 | exit 1 26 | fi 27 | } 28 | -------------------------------------------------------------------------------- /inline_functions/before_final_return_message.sh: -------------------------------------------------------------------------------- 1 | function before_final_return_message() { 2 | echo "" 3 | echo -e "${INFO_FONT}BEFORE you hit return:${NC}" 4 | echo " *** Unlock your phone and plug it into your computer" 5 | echo " Trust computer if asked" 6 | echo "" 7 | echo -e "${INFO_FONT}AFTER you hit return, Xcode will open automatically${NC}" 8 | echo " For new phone or new watch (never used with Xcode)," 9 | echo " review Developer Mode Information:" 10 | echo -e " https://loopkit.github.io/loopdocs/build/step14/#prepare-your-phone-and-watch" 11 | echo "" 12 | echo " For phones that have Developer Mode enabled continue with these steps" 13 | echo " Upper middle of Xcode:" 14 | echo " Confirm your phone or simulator choice is selected" 15 | echo " Upper right of Xcode:" 16 | echo " Wait for packages to finish being copied or downloaded" 17 | echo " When you see indexing, you can start the build" 18 | echo " Click on Play button to build and run on the selected device" 19 | } 20 | 21 | function after_final_return_message() { 22 | section_divider 23 | echo "If you need to find this download in a terminal, copy and paste the next line:" 24 | echo "" 25 | echo "cd ${LOCAL_DIR}/${REPO_NAME}" 26 | } 27 | -------------------------------------------------------------------------------- /inline_functions/utility_scripts.sh: -------------------------------------------------------------------------------- 1 | function utility_scripts { 2 | section_separator 3 | echo -e "${INFO_FONT}These utility scripts automate several cleanup actions${NC}" 4 | echo -e "" 5 | echo -e " 1. Delete Old Downloads:" 6 | echo -e " This will keep the most recent download for each build type" 7 | echo -e " It asks before deleting any folders" 8 | echo -e " 2. Clean Derived Data:" 9 | echo -e " Free space on your disk from old Xcode builds." 10 | echo -e " You should quit Xcode before running this script." 11 | echo -e " 3. Xcode Cleanup (The Big One):" 12 | echo -e " Clears more disk space filled up by using Xcode." 13 | echo -e " * Use after uninstalling Xcode prior to new installation" 14 | echo -e " * It can free up a substantial amount of disk space" 15 | echo -e " You should quit Xcode before running this script." 16 | echo -e " 4. Clean Profiles:" 17 | echo -e " Deletes any provisioning profiles on your Mac" 18 | echo -e " * Xcode will generate new ones" 19 | echo -e " * Ensures the next app you build with Xcode will last a year" 20 | section_divider 21 | echo -e "${INFO_FONT}Pay attention - quit Xcode before selecting some options${NC}" 22 | section_divider 23 | 24 | options=( 25 | "Delete Old Downloads" 26 | "Clean Derived Data (Quit Xcode)" 27 | "Xcode Cleanup (Quit Xcode)" 28 | "Clean Profiles" 29 | "Return to Menu" 30 | ) 31 | actions=( 32 | "run_script 'DeleteOldDownloads.sh'" 33 | "run_script 'CleanDerived.sh'" 34 | "run_script 'XcodeClean.sh'" 35 | "run_script 'CleanProfiles.sh'" 36 | return 37 | ) 38 | menu_select "${options[@]}" "${actions[@]}" 39 | return_when_ready 40 | } -------------------------------------------------------------------------------- /inline_functions/build_warning.sh: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # warning used by all scripts that build an app 3 | ############################################################ 4 | 5 | function open_source_warning() { 6 | # Skip open source warning if opted out using env variable or this script is run from another script 7 | if [ "${SKIP_OPEN_SOURCE_WARNING}" = "1" ] || [ "$0" = "_" ]; then return; fi 8 | 9 | local documentation_link="${1:-}" 10 | 11 | section_separator 12 | 13 | echo -e "${INFO_FONT}*** IMPORTANT ***${NC}\n" 14 | echo -e "This project is:" 15 | echo -e "${INFO_FONT} Open Source software" 16 | echo -e " Not \"approved\" for therapy${NC}" 17 | echo -e "" 18 | echo -e " You take full responsibility when you build" 19 | echo -e " or run an open source app, and" 20 | echo -e " ${INFO_FONT}you do so at your own risk.${NC}" 21 | echo -e "" 22 | echo -e "To increase (decrease) font size" 23 | echo -e " Hold down the CMD key and hit + (-)" 24 | echo -e "\n${INFO_FONT}By typing 1 and ENTER, you indicate you understand" 25 | echo -e "\n--------------------------------\n${NC}" 26 | 27 | options=("Agree" "Cancel") 28 | select opt in "${options[@]}"; do 29 | case $opt in 30 | "Agree") 31 | break 32 | ;; 33 | "Cancel") 34 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 35 | exit_script 36 | ;; 37 | *) 38 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 39 | invalid_entry 40 | exit_script 41 | ;; 42 | esac 43 | done 44 | 45 | # Warning has been issued 46 | SKIP_OPEN_SOURCE_WARNING=1 47 | 48 | echo -e "${NC}\n\n\n\n" 49 | } 50 | -------------------------------------------------------------------------------- /src/BuildLoopCaregiver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopCaregiver.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | BUILD_DIR=~/Downloads/"BuildLoop" 9 | OVERRIDE_FILE=LoopConfigOverride.xcconfig 10 | DEV_TEAM_SETTING_NAME="LOOP_DEVELOPMENT_TEAM" 11 | 12 | #!inline build_functions.sh 13 | 14 | 15 | ############################################################ 16 | # The rest of this is specific to the particular script 17 | ############################################################ 18 | 19 | open_source_warning 20 | 21 | 22 | ############################################################ 23 | # Welcome & Branch Selection 24 | ############################################################ 25 | 26 | URL_THIS_SCRIPT="https://github.com/LoopKit/LoopCaregiver.git" 27 | 28 | function choose_dev_branch() { 29 | branch_select ${URL_THIS_SCRIPT} dev 30 | } 31 | 32 | if [ -z "$CUSTOM_BRANCH" ]; then 33 | section_separator 34 | echo -e "\n ${INFO_FONT}You are running the script for LoopCaregiver (LCG)" 35 | echo -e " This app is under development and may require frequent builds${NC}" 36 | echo -e " You need Xcode and Xcode command line tools installed" 37 | echo -e "" 38 | echo -e " If you have not read this section of LoopDocs - please review before continuing" 39 | echo -e " https://loopkit.github.io/loopdocs/nightscout/remote-overrides" 40 | echo -e "" 41 | echo -e " If you have not joined zulipchat Loop Caregiver App stream - do so now" 42 | echo -e " https://loop.zulipchat.com/#narrow/stream/358458-Loop-Caregiver-App" 43 | 44 | options=("Continue" "$(exit_or_return_menu)") 45 | actions=("choose_dev_branch" "exit_script") 46 | menu_select "${options[@]}" "${actions[@]}" 47 | else 48 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 49 | fi 50 | 51 | ############################################################ 52 | # Standard Build train 53 | ############################################################ 54 | 55 | standard_build_train 56 | 57 | 58 | ############################################################ 59 | # Open Xcode 60 | ############################################################ 61 | 62 | section_divider 63 | before_final_return_message 64 | echo -e "" 65 | return_when_ready 66 | cd $REPO_NAME 67 | xed . 68 | after_final_return_message 69 | exit_script 70 | -------------------------------------------------------------------------------- /src/BuildxDrip4iOS.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildxDrip4iOS.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | BUILD_DIR=~/Downloads/BuildxDrip4iOS 9 | OVERRIDE_FILE=xDripConfigOverride.xcconfig 10 | DEV_TEAM_SETTING_NAME="XDRIP_DEVELOPMENT_TEAM" 11 | 12 | # value of 2 adds additional line to Override file in repo 13 | USE_OVERRIDE_IN_REPO="2" 14 | ADDED_LINE_FOR_OVERRIDE=("MAIN_APP_DISPLAY_NAME=xDrip4iO5" \ 15 | "MAIN_APP_BUNDLE_IDENTIFIER=com.\$(DEVELOPMENT_TEAM).xdripswift") 16 | 17 | # sub modules are not required 18 | CLONE_SUB_MODULES="0" 19 | 20 | #!inline build_functions.sh 21 | 22 | 23 | ############################################################ 24 | # The rest of this is specific to the particular script 25 | ############################################################ 26 | 27 | open_source_warning 28 | 29 | 30 | ############################################################ 31 | # Welcome & Branch Selection 32 | ############################################################ 33 | 34 | URL_THIS_SCRIPT="https://github.com/JohanDegraeve/xdripswift.git" 35 | 36 | function choose_main_branch() { 37 | branch_select ${URL_THIS_SCRIPT} master xDrip4iOS 38 | } 39 | 40 | if [ -z "$CUSTOM_BRANCH" ]; then 41 | section_separator 42 | echo -e "\n${INFO_FONT}You are running the script to build xDrip4iOS${NC}" 43 | echo -e " You need Xcode and Xcode command line tools installed" 44 | echo -e "" 45 | echo -e " If you have not read the docs - please review before continuing" 46 | echo -e " https://xdrip4ios.readthedocs.io/en/latest/" 47 | section_divider 48 | 49 | options=("Continue" "$(exit_or_return_menu)") 50 | actions=("choose_main_branch" "exit_script") 51 | menu_select "${options[@]}" "${actions[@]}" 52 | else 53 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 54 | fi 55 | 56 | ############################################################ 57 | # Standard Build train 58 | ############################################################ 59 | 60 | standard_build_train 61 | 62 | ############################################################ 63 | # Open Xcode 64 | ############################################################ 65 | 66 | section_divider 67 | before_final_return_message 68 | echo -e "" 69 | return_when_ready 70 | cd $REPO_NAME 71 | xed . 72 | after_final_return_message 73 | exit_script 74 | -------------------------------------------------------------------------------- /patch/g6g7_upload_readings.patch: -------------------------------------------------------------------------------- 1 | Submodule CGMBLEKit contains modified content 2 | diff --git a/CGMBLEKit/CGMBLEKit/TransmitterManager.swift b/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 3 | index 4942087..371b7bd 100644 4 | --- a/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 5 | +++ b/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 6 | @@ -22,7 +22,7 @@ public struct TransmitterManagerState: RawRepresentable, Equatable { 7 | 8 | public var shouldSyncToRemoteService: Bool 9 | 10 | - public init(transmitterID: String, shouldSyncToRemoteService: Bool = false) { 11 | + public init(transmitterID: String, shouldSyncToRemoteService: Bool = true) { 12 | self.transmitterID = transmitterID 13 | self.shouldSyncToRemoteService = shouldSyncToRemoteService 14 | } 15 | Submodule G7SensorKit contains modified content 16 | diff --git a/G7SensorKit/G7SensorKit/G7CGMManager/G7CGMManagerState.swift b/G7SensorKit/G7SensorKit/G7CGMManager/G7CGMManagerState.swift 17 | index 948b02f..95538dd 100644 18 | --- a/G7SensorKit/G7SensorKit/G7CGMManager/G7CGMManagerState.swift 19 | +++ b/G7SensorKit/G7SensorKit/G7CGMManager/G7CGMManagerState.swift 20 | @@ -18,7 +18,7 @@ public struct G7CGMManagerState: RawRepresentable, Equatable { 21 | public var latestReading: G7GlucoseMessage? 22 | public var latestReadingTimestamp: Date? 23 | public var latestConnect: Date? 24 | - public var uploadReadings: Bool = false 25 | + public var uploadReadings: Bool = true 26 | 27 | init() { 28 | } 29 | @@ -31,7 +31,7 @@ public struct G7CGMManagerState: RawRepresentable, Equatable { 30 | } 31 | self.latestReadingTimestamp = rawValue["latestReadingTimestamp"] as? Date 32 | self.latestConnect = rawValue["latestConnect"] as? Date 33 | - self.uploadReadings = rawValue["uploadReadings"] as? Bool ?? false 34 | + self.uploadReadings = rawValue["uploadReadings"] as? Bool ?? true 35 | } 36 | 37 | public var rawValue: RawValue { 38 | diff --git a/G7SensorKit/G7SensorKitUI/Views/G7SettingsViewModel.swift b/G7SensorKit/G7SensorKitUI/Views/G7SettingsViewModel.swift 39 | index 8513d2b..e2e275c 100644 40 | --- a/G7SensorKit/G7SensorKitUI/Views/G7SettingsViewModel.swift 41 | +++ b/G7SensorKit/G7SensorKitUI/Views/G7SettingsViewModel.swift 42 | @@ -23,7 +23,7 @@ class G7SettingsViewModel: ObservableObject { 43 | @Published private(set) var activatedAt: Date? 44 | @Published private(set) var lastConnect: Date? 45 | @Published private(set) var latestReadingTimestamp: Date? 46 | - @Published var uploadReadings: Bool = false { 47 | + @Published var uploadReadings: Bool = true { 48 | didSet { 49 | cgmManager.uploadReadings = uploadReadings 50 | } 51 | -------------------------------------------------------------------------------- /patch_cto/cto_main_LoopKit.patch: -------------------------------------------------------------------------------- 1 | diff --git a/LoopKit/InsulinKit/InsulinMath.swift b/LoopKit/InsulinKit/InsulinMath.swift 2 | index 6efd2bd..094fee4 100644 3 | --- a/LoopKit/InsulinKit/InsulinMath.swift 4 | +++ b/LoopKit/InsulinKit/InsulinMath.swift 5 | @@ -40,10 +40,23 @@ extension DoseEntry { 6 | } 7 | 8 | // Consider doses within the delta time window as momentary 9 | + //ken changes 10 | + //implement user set negative basal multiplier 11 | + var negativeBasalMultiplier = UserDefaults.standard.double(forKey: "negativeBasalMultiplier") 12 | + // if user has not modified settings, this value reports as 0 initially 13 | + // Default shows as 100%, aka, same as unmodified code 14 | + if negativeBasalMultiplier == 0 { 15 | + negativeBasalMultiplier = 1 16 | + } 17 | + var modifiednetBasalUnits = netBasalUnits 18 | + if netBasalUnits < 0.0 { 19 | + modifiednetBasalUnits = netBasalUnits * negativeBasalMultiplier 20 | + } 21 | + //this used netBasalUnits as multiplier originally 22 | if endDate.timeIntervalSince(startDate) <= 1.05 * delta { 23 | - return netBasalUnits * model.percentEffectRemaining(at: time) 24 | + return modifiednetBasalUnits * model.percentEffectRemaining(at: time) 25 | } else { 26 | - return netBasalUnits * continuousDeliveryInsulinOnBoard(at: date, model: model, delta: delta) 27 | + return modifiednetBasalUnits * continuousDeliveryInsulinOnBoard(at: date, model: model, delta: delta) 28 | } 29 | } 30 | 31 | @@ -77,11 +90,23 @@ extension DoseEntry { 32 | } 33 | 34 | // Consider doses within the delta time window as momentary 35 | - if endDate.timeIntervalSince(startDate) <= 1.05 * delta { 36 | - return netBasalUnits * -insulinSensitivity * (1.0 - model.percentEffectRemaining(at: time)) 37 | - } else { 38 | - return netBasalUnits * -insulinSensitivity * continuousDeliveryGlucoseEffect(at: date, model: model, delta: delta) 39 | - } 40 | + //ken changes 41 | + //if net basal is negative use a mulitplier (0-1) 42 | + //modified in user settings 43 | + var negativeBasalMultiplier = UserDefaults.standard.double(forKey: "negativeBasalMultiplier") 44 | + if negativeBasalMultiplier == 0 { 45 | + negativeBasalMultiplier = 1 46 | + } 47 | + var modifiednetBasalUnits = netBasalUnits 48 | + if netBasalUnits < 0.0 { 49 | + modifiednetBasalUnits = netBasalUnits * negativeBasalMultiplier 50 | + } 51 | + //originally used netBasalUnits 52 | + if endDate.timeIntervalSince(startDate) <= 1.05 * delta { 53 | + return modifiednetBasalUnits * -insulinSensitivity * (1.0 - model.percentEffectRemaining(at: time)) 54 | + } else { 55 | + return modifiednetBasalUnits * -insulinSensitivity * continuousDeliveryGlucoseEffect(at: date, model: model, delta: delta) 56 | + } 57 | } 58 | 59 | func trimmed(from start: Date? = nil, to end: Date? = nil, syncIdentifier: String? = nil) -> DoseEntry { 60 | -------------------------------------------------------------------------------- /src/TrioBuildSelectScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script TrioBuildSelectScript.sh 2 | 3 | #!inline common.sh 4 | #!inline build_warning.sh 5 | #!inline run_script.sh 6 | #!inline utility_scripts.sh 7 | 8 | # Set default values only if they haven't been defined as environment variables 9 | : ${SCRIPT_BRANCH:="main"} 10 | 11 | function placeholder() { 12 | section_divider 13 | echo -e " The feature is not available, coming soon" 14 | echo -e " This is a placeholder" 15 | return 16 | } 17 | 18 | ############################################################ 19 | # The rest of this is specific to the particular script 20 | ############################################################ 21 | 22 | # use app_name instead of hard-coded strings 23 | app_name="Trio" 24 | 25 | FIRST_TIME="1" 26 | SKIP_OPEN_SOURCE_WARNING="0" 27 | 28 | function first_time_menu() { 29 | section_separator 30 | echo -e "${INFO_FONT}Welcome to the Loop and Learn\n ${app_name} Build-Select Script\n${NC}" 31 | echo "Choose from one of these options:" 32 | echo " 1 Download and Build ${app_name}" 33 | echo " 2 Download and Build Related Apps" 34 | echo " 3 Run Maintenance Utilities" 35 | echo " 4 Exit Script" 36 | echo "" 37 | echo "After completing a given option, you can choose another or exit the script" 38 | FIRST_TIME="0" 39 | } 40 | 41 | ############################################################ 42 | # Welcome & What to do selection 43 | ############################################################ 44 | 45 | while true; do 46 | if [ "${FIRST_TIME}" = "1" ]; then 47 | first_time_menu 48 | fi 49 | section_divider 50 | 51 | options=(\ 52 | "Build ${app_name}" \ 53 | "Build Related Apps" \ 54 | "Maintenance Utilities" \ 55 | "Exit Script") 56 | actions=(\ 57 | "WHICH=${app_name}" \ 58 | "WHICH=OtherApps" \ 59 | "WHICH=UtilityScripts" \ 60 | "exit_script") 61 | menu_select "${options[@]}" "${actions[@]}" 62 | 63 | if [ "$WHICH" = "${app_name}" ]; then 64 | 65 | # Issue Warning if not done previously 66 | open_source_warning 67 | 68 | run_script "Build${app_name}.sh" $CUSTOM_BRANCH 69 | 70 | 71 | elif [ "$WHICH" = "OtherApps" ]; then 72 | 73 | # Issue Warning if not done previously 74 | open_source_warning 75 | 76 | section_separator 77 | echo -e "Select the app you want to build" 78 | echo -e " Each selection will indicate documentation links" 79 | echo -e " Please read the documentation before using the app" 80 | echo -e "" 81 | options=(\ 82 | "Build Loop Follow" \ 83 | "Build xDrip4iOS" \ 84 | "Return to Menu") 85 | actions=(\ 86 | "WHICH=LoopFollow" \ 87 | "WHICH=xDrip4iOS" \ 88 | return) 89 | menu_select "${options[@]}" "${actions[@]}" 90 | if [ "$WHICH" = "LoopFollow" ]; then 91 | run_script "BuildLoopFollow.sh" $CUSTOM_BRANCH 92 | elif [ "$WHICH" = "xDrip4iOS" ]; then 93 | run_script "BuildxDrip4iOS.sh" $CUSTOM_BRANCH 94 | fi 95 | 96 | elif [ "$WHICH" = "UtilityScripts" ]; then 97 | utility_scripts 98 | fi 99 | done 100 | -------------------------------------------------------------------------------- /src/BuildLoopFollow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoopFollow.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | BUILD_DIR=~/Downloads/BuildLoopFollow 9 | OVERRIDE_FILE=LoopFollowConfigOverride.xcconfig 10 | DEV_TEAM_SETTING_NAME="LF_DEVELOPMENT_TEAM" 11 | CLONE_SUB_MODULES="0" 12 | 13 | #!inline build_functions.sh 14 | #!inline loopfollow_functions.sh 15 | 16 | ############################################################ 17 | # The rest of this is specific to the particular script 18 | ############################################################ 19 | 20 | open_source_warning 21 | 22 | 23 | ############################################################ 24 | # Welcome & Branch Selection 25 | ############################################################ 26 | 27 | URL_THIS_SCRIPT="https://github.com/loopandlearn/LoopFollow.git" 28 | 29 | function choose_main_branch() { 30 | branch_select ${URL_THIS_SCRIPT} main 31 | } 32 | 33 | function choose_second() { 34 | branch_select "https://github.com/loopandlearn/LoopFollow_Second.git" main 35 | } 36 | 37 | function choose_third() { 38 | branch_select "https://github.com/loopandlearn/LoopFollow_Third.git" main 39 | } 40 | 41 | function choose_dev_branch() { 42 | branch_select ${URL_THIS_SCRIPT} dev 43 | } 44 | 45 | if [ -z "$CUSTOM_BRANCH" ]; then 46 | section_separator 47 | echo -e "${INFO_FONT}You are running the script to build Loop Follow${NC}" 48 | echo -e " You need Xcode and Xcode command line tools installed" 49 | echo -e "" 50 | echo -e "${INFO_FONT}STOP - Please read this updated information${NC}" 51 | echo -e "" 52 | echo -e "You can build main or dev branch of Loop Follow with this script" 53 | echo -e "For main branch, you can choose multiple instances" 54 | echo -e " - useful if you follow more than one looper" 55 | echo -e "" 56 | echo -e "${INFO_FONT}You can choose a custom app/display name${NC}" 57 | echo -e "" 58 | echo -e "These choices build the main branch" 59 | echo -e " - 'main branch': Use this to build the primary version of Loop Follow" 60 | echo -e " - 'Second LoopFollow': Use this for a second looper" 61 | echo -e " - 'Third LoopFollow': Use this for a third looper" 62 | echo -e "" 63 | echo -e " - 'dev branch': Choose only when a feature is being tested in dev" 64 | echo -e "" 65 | echo -e "Documentation: https://www.loopandlearn.org/loop-follow/" 66 | echo -e "" 67 | return_when_ready 68 | section_divider 69 | 70 | options=("main branch" "Second LoopFollow app" "Third LoopFollow app" "dev branch" "$(exit_or_return_menu)") 71 | actions=("choose_main_branch" "choose_second" "choose_third" "choose_dev_branch" "exit_script") 72 | menu_select "${options[@]}" "${actions[@]}" 73 | else 74 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 75 | fi 76 | 77 | 78 | ############################################################ 79 | # Standard Build train 80 | ############################################################ 81 | 82 | standard_build_train 83 | loop_follow_display_name_config_override 84 | 85 | 86 | ############################################################ 87 | # Open Xcode 88 | ############################################################ 89 | 90 | section_divider 91 | before_final_return_message 92 | echo -e "" 93 | return_when_ready 94 | cd $REPO_NAME 95 | xed . 96 | after_final_return_message 97 | exit_script 98 | -------------------------------------------------------------------------------- /src/BuildSelectScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildSelectScript.sh 2 | 3 | #!inline common.sh 4 | #!inline build_warning.sh 5 | #!inline run_script.sh 6 | #!inline utility_scripts.sh 7 | 8 | # Set default values only if they haven't been defined as environment variables 9 | : ${SCRIPT_BRANCH:="main"} 10 | 11 | function placeholder() { 12 | section_divider 13 | echo -e " The feature is not available, coming soon" 14 | echo -e " This is a placeholder" 15 | return 16 | } 17 | 18 | ############################################################ 19 | # The rest of this is specific to the particular script 20 | ############################################################ 21 | 22 | FIRST_TIME="1" 23 | SKIP_OPEN_SOURCE_WARNING="0" 24 | 25 | function first_time_menu() { 26 | section_separator 27 | echo -e "${INFO_FONT}Welcome to the Loop and Learn\n Build-Select Script\n${NC}" 28 | echo "Choose from one of these options:" 29 | echo " 1 Download and Build Loop" 30 | echo " 2 Download and Build Related Apps" 31 | echo " 3 Run Maintenance Utilities" 32 | echo " 4 Run Customization Select" 33 | echo " 5 Exit Script" 34 | echo "" 35 | echo "After completing a given option, you can choose another or exit the script" 36 | FIRST_TIME="0" 37 | } 38 | 39 | ############################################################ 40 | # Welcome & What to do selection 41 | ############################################################ 42 | 43 | while true; do 44 | if [ "${FIRST_TIME}" = "1" ]; then 45 | first_time_menu 46 | fi 47 | section_divider 48 | 49 | options=(\ 50 | "Build Loop" \ 51 | "Build Related Apps" \ 52 | "Maintenance Utilities" \ 53 | "Customization Select" \ 54 | "Exit Script") 55 | actions=(\ 56 | "WHICH=Loop" \ 57 | "WHICH=OtherApps" \ 58 | "WHICH=UtilityScripts" \ 59 | "WHICH=CustomizationScripts" \ 60 | "exit_script") 61 | menu_select "${options[@]}" "${actions[@]}" 62 | 63 | if [ "$WHICH" = "Loop" ]; then 64 | 65 | # Issue Warning if not done previously 66 | open_source_warning 67 | 68 | run_script "BuildLoop.sh" $CUSTOM_BRANCH 69 | 70 | 71 | elif [ "$WHICH" = "OtherApps" ]; then 72 | 73 | # Issue Warning if not done previously 74 | open_source_warning 75 | 76 | section_separator 77 | echo -e "Select the app you want to build" 78 | echo -e " Each selection will indicate documentation links" 79 | echo -e " Please read the documentation before using the app" 80 | echo -e "" 81 | options=(\ 82 | "Build Loop Follow" \ 83 | "Build LoopCaregiver" \ 84 | "Build xDrip4iOS" \ 85 | "Return to Menu") 86 | actions=(\ 87 | "WHICH=LoopFollow" \ 88 | "WHICH=LoopCaregiver" \ 89 | "WHICH=xDrip4iOS" \ 90 | return) 91 | menu_select "${options[@]}" "${actions[@]}" 92 | if [ "$WHICH" = "LoopFollow" ]; then 93 | run_script "BuildLoopFollow.sh" $CUSTOM_BRANCH 94 | elif [ "$WHICH" = "LoopCaregiver" ]; then 95 | run_script "BuildLoopCaregiver.sh" $CUSTOM_BRANCH 96 | elif [ "$WHICH" = "xDrip4iOS" ]; then 97 | run_script "BuildxDrip4iOS.sh" $CUSTOM_BRANCH 98 | fi 99 | 100 | elif [ "$WHICH" = "UtilityScripts" ]; then 101 | utility_scripts 102 | else 103 | run_script "CustomizationSelect.sh" $CUSTOM_BRANCH 104 | fi 105 | done 106 | -------------------------------------------------------------------------------- /src/BuildLoop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildLoop.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | # use app_name instead of hard-coded strings 9 | app_name="Loop" 10 | 11 | BUILD_DIR=~/Downloads/"Build${app_name}" 12 | OVERRIDE_FILE=LoopConfigOverride.xcconfig 13 | DEV_TEAM_SETTING_NAME="LOOP_DEVELOPMENT_TEAM" 14 | 15 | #!inline build_functions.sh 16 | 17 | ############################################################ 18 | # The rest of this is specific to the particular script 19 | ############################################################ 20 | 21 | # Set default values only if they haven't been defined as environment variables 22 | : ${SCRIPT_BRANCH:="main"} 23 | 24 | open_source_warning 25 | 26 | ############################################################ 27 | # Welcome & Branch Selection 28 | ############################################################ 29 | 30 | URL_THIS_SCRIPT="https://github.com/LoopKit/LoopWorkspace.git" 31 | 32 | function select_main() { 33 | branch_select ${URL_THIS_SCRIPT} main Loop 34 | } 35 | 36 | function select_dev() { 37 | branch_select ${URL_THIS_SCRIPT} dev Loop_dev 38 | } 39 | 40 | # Keep this for when we need a special branch name 41 | # If not used, make this empty string and comment out the menu option 42 | special_branch_name="" 43 | 44 | 45 | if [ -z "$CUSTOM_BRANCH" ]; then 46 | while [ -z "$BRANCH" ]; do 47 | section_separator 48 | echo -e "${INFO_FONT}You are running the script to build ${app_name}${NC}" 49 | echo -e "${INFO_FONT}You should be familiar with LoopDocs found at:${NC}" 50 | echo 51 | echo -e " https://loopdocs.org" 52 | echo 53 | echo -e " ${INFO_FONT}Option 1: ${app_name} main branch is recommended${NC}" 54 | echo -e "" 55 | echo -e " If you choose dev branch, you should be prepared to build frequently" 56 | echo " You should be following zulipchat and have read:" 57 | echo 58 | echo " https://loopkit.github.io/loopdocs/version/development/#whats-going-on-in-the-dev-branch" 59 | echo 60 | echo -e " Before you continue, please ensure" 61 | echo -e " you have Xcode and Xcode command line tools installed\n" 62 | section_divider 63 | 64 | options=(\ 65 | "${app_name} main" \ 66 | "${app_name} dev" \ 67 | # "${app_name} ${special_branch_name}" \ 68 | "$(exit_or_return_menu)") 69 | actions=(\ 70 | "select_main" \ 71 | "select_dev" \ 72 | # "select_special_branch" \ 73 | "exit_script") 74 | menu_select "${options[@]}" "${actions[@]}" 75 | done 76 | else 77 | section_separator 78 | echo -e "You are about to download ${CUSTOM_BRANCH} branch from" 79 | echo -e " ${CUSTOM_URL:-${URL_THIS_SCRIPT}}\n" 80 | return_when_ready 81 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 82 | fi 83 | 84 | ############################################################ 85 | # Standard Build train 86 | ############################################################ 87 | 88 | standard_build_train 89 | 90 | ############################################################ 91 | # Open Xcode 92 | ############################################################ 93 | 94 | section_divider 95 | before_final_return_message 96 | echo -e "" 97 | return_when_ready 98 | cd $REPO_NAME 99 | xed . 100 | after_final_return_message 101 | exit_script 102 | 103 | -------------------------------------------------------------------------------- /custom_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script sets custom values for scripts, like BuildLoop. 4 | # It MUST be run using source in order to affect the build scripts. 5 | # For example: source custom_config.sh && ./BuildLoop.sh 6 | 7 | # Clear values before setting them below 8 | unset SCRIPT_BRANCH 9 | unset LOCAL_SCRIPT 10 | unset FRESH_CLONE 11 | unset CLONE_STATUS 12 | unset SKIP_OPEN_SOURCE_WARNING 13 | unset CUSTOM_URL 14 | unset CUSTOM_BRANCH 15 | unset CUSTOM_MACOS_VER 16 | unset CUSTOM_XCODE_VER 17 | unset PATCH_BRANCH 18 | unset PATCH_REPO 19 | unset LOCAL_PATCH_FOLDER 20 | unset CUSTOMIZATION_DEBUG 21 | 22 | # To Test Scripts as they exist on github 23 | # SCRIPT_BRANCH is the branch scripts will be sourced from 24 | # Uncomment line and replace main with branch you are testing 25 | #export SCRIPT_BRANCH="main" 26 | 27 | # LOCAL_SCRIPT can be set to 1 to run scripts from the local directory 28 | # If not set or set to 0, scripts will be sourced from GitHub 29 | # Uncomment the line 30 | # Set the value to 1 to run scripts locally 31 | #export LOCAL_SCRIPT="1" 32 | 33 | # FRESH_CLONE (of 0) lets you use an existing LoopWorkspace clone (saves time) 34 | # Uncomment the line 35 | # Terminal must be one level higher than an existing LoopWorkspace folder 36 | #export FRESH_CLONE="0" 37 | 38 | # CLONE_STATUS can be set to 0 for success (default) or 1 for error 39 | # Uncomment only to trigger an error in the clone check 40 | # Pair this with FRESH_CLONE="0" 41 | #export CLONE_STATUS="1" 42 | 43 | # SKIP_OPEN_SOURCE_WARNING can be set to 1 to skip the open source warning 44 | # Uncomment the line 45 | # Set the value to 1 to skip the open source warning 46 | #export SKIP_OPEN_SOURCE_WARNING="1" 47 | 48 | # CUSTOM_BRANCH overrides the branch used for git clone 49 | # Uncomment the line 50 | # Set value to branch name, for example libre 51 | #export CUSTOM_BRANCH=libre 52 | 53 | # CUSTOM_URL overrides the repo url 54 | # Uncomment the line 55 | # Set the repo to be cloned 56 | #export CUSTOM_URL=https://github.com/bjorkert/LoopWorkspace.git 57 | 58 | # CUSTOM_XCODE_VER overrides the detected Xcode version 59 | # Uncomment the line 60 | # Set the version to be simulated 61 | #export CUSTOM_XCODE_VER=14.1 62 | 63 | # CUSTOM_MACOS_VER overrides the detected macOS version 64 | # Uncomment the line 65 | # Set the version to be simulated 66 | #export CUSTOM_MACOS_VER=12.9 67 | 68 | # DELETE_SELECTED_FOLDERS override to just report what folders would be deleted 69 | # Uncomment the line 70 | # Default is 1 (really delete) 71 | #export DELETE_SELECTED_FOLDERS=0 72 | 73 | # PATCH_BRANCH is the branch from which patches will be sourced 74 | # Uncomment the line and replace "main" with the branch you are using 75 | #export PATCH_BRANCH="main" 76 | 77 | # PATCH_REPO is the repository URL from which patches will be sourced 78 | # Uncomment the line and replace with your own repository URL if not using the default 79 | #export PATCH_REPO="https://github.com/loopandlearn/customization.git" 80 | 81 | # LOCAL_PATCH_FOLDER is the local directory from which patches will be sourced 82 | # If not set or empty, patches will be downloaded from the PATCH_REPO 83 | # Uncomment the line and replace with your own local directory if not using the default 84 | # The path should be absolute or relative to the current script's directory 85 | #export LOCAL_PATCH_FOLDER="/path/to/your/local/patch/folder" 86 | 87 | # CUSTOMIZATION_DEBUG determines the verbosity of the debug output 88 | # Set to 1 to enable debug (verbose output) mode at the beginning of the script 89 | # Set to 2 to enable debug (verbose output) mode for every refresh 90 | # Uncomment the line and set the value to your preference 91 | #export CUSTOMIZATION_DEBUG=1 -------------------------------------------------------------------------------- /inline_functions/building_verify_version.sh: -------------------------------------------------------------------------------- 1 | #This should be the latest iOS version 2 | #This is the highest version we expect users to have on their iPhones 3 | LATEST_IOS_VER="26.1" 4 | 5 | #This should be the lowest xcode version required to build to LATEST_IOS_VER 6 | LOWEST_XCODE_VER="16.4" 7 | 8 | #This should be the latest known xcode version 9 | #LOWEST_XCODE_VER and LATEST_XCODE_VER will probably be equal but we should have suport for a span of these 10 | LATEST_XCODE_VER="26.1.1" 11 | 12 | #This is the lowest version of macOS required to run LOWEST_XCODE_VER 13 | LOWEST_MACOS_VER="15.3" 14 | 15 | # The compare_versions function takes two version strings as input arguments, 16 | # sorts them in ascending order using the sort command with the -V flag (version sorting), 17 | # and returns the first version (i.e., the lowest one) using head -n1. 18 | # 19 | # Example: 20 | # compare_versions "1.2.3" "1.1.0" will return "1.1.0" 21 | function compare_versions() { 22 | printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1 23 | } 24 | 25 | function check_versions() { 26 | section_divider 27 | echo "Verifying Xcode and macOS versions..." 28 | 29 | if ! command -v xcodebuild >/dev/null; then 30 | echo " Xcode not found. Please install Xcode and try again." 31 | exit_or_return_menu 32 | fi 33 | 34 | if [ -n "$CUSTOM_XCODE_VER" ]; then 35 | XCODE_VER="$CUSTOM_XCODE_VER" 36 | else 37 | XCODE_VER=$(xcodebuild -version | awk '/Xcode/{print $NF}') 38 | fi 39 | 40 | if [ -n "$CUSTOM_MACOS_VER" ]; then 41 | MACOS_VER="$CUSTOM_MACOS_VER" 42 | else 43 | MACOS_VER=$(sw_vers -productVersion) 44 | fi 45 | 46 | echo " Xcode found: Version $XCODE_VER" 47 | 48 | # Check if Xcode version is greater than the latest known version 49 | if [ "$(compare_versions "$XCODE_VER" "$LATEST_XCODE_VER")" = "$LATEST_XCODE_VER" ] && [ "$XCODE_VER" != "$LATEST_XCODE_VER" ]; then 50 | echo "" 51 | echo "You have a newer Xcode version ($XCODE_VER) than" 52 | echo " the latest released version known by this script ($LATEST_XCODE_VER)." 53 | echo "You can probably continue; but if you have problems, refer to" 54 | echo " https://developer.apple.com/support/xcode/" 55 | 56 | options=("Continue" "$(exit_or_return_menu)") 57 | actions=("return" "exit_script") 58 | menu_select "${options[@]}" "${actions[@]}" 59 | # Check if Xcode version is less than the lowest required version 60 | elif [ "$(compare_versions "$XCODE_VER" "$LOWEST_XCODE_VER")" = "$XCODE_VER" ] && [ "$XCODE_VER" != "$LOWEST_XCODE_VER" ]; then 61 | if [ "$(compare_versions "$MACOS_VER" "$LOWEST_MACOS_VER")" != "$LOWEST_MACOS_VER" ]; then 62 | echo "" 63 | echo "Your macOS version ($MACOS_VER) is lower than $LOWEST_MACOS_VER" 64 | echo " required to build for iOS $LATEST_IOS_VER." 65 | echo "Please update macOS to version $LOWEST_MACOS_VER or later." 66 | echo "" 67 | echo "If you can't update, follow the GitHub build option here:" 68 | echo " https://loopkit.github.io/loopdocs/gh-actions/gh-overview/" 69 | fi 70 | 71 | echo "" 72 | echo "You need to upgrade Xcode to version $LOWEST_XCODE_VER or later to build for iOS $LATEST_IOS_VER." 73 | echo "If your iOS is at a lower version, refer to the compatibility table in LoopDocs" 74 | echo " https://loopkit.github.io/loopdocs/build/xcode-version/#compatible-versions" 75 | 76 | options=("Continue with lower iOS version" "$(exit_or_return_menu)") 77 | actions=("return" "exit_script") 78 | menu_select "${options[@]}" "${actions[@]}" 79 | else 80 | echo "Your Xcode version can build up to iOS $LATEST_IOS_VER." 81 | fi 82 | } 83 | -------------------------------------------------------------------------------- /inline_functions/loopfollow_functions.sh: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Special functions used by LoopFollow build script 3 | ############################################################ 4 | 5 | function display_name_suggestion() { 6 | # Display this message only once per call to BuildLoopFollow.sh 7 | echo "" 8 | if [ "${SKIP_DISPLAY_NAME_INFORMATION}" = "1" ]; then return; fi 9 | echo "The display name replaces the follow app name on the phone" 10 | echo " and can be displayed on the home screen of the follow app" 11 | echo "" 12 | echo " To assist in finding the renamed app in iOS Settings," 13 | echo " you might want to use LF as a prefix." 14 | echo " For example: LF George" 15 | echo "" 16 | SKIP_DISPLAY_NAME_INFORMATION=1 17 | } 18 | 19 | function loop_follow_display_name_config_override() { 20 | cd "$REPO_NAME" 21 | # Define the base file names 22 | local current_file="LoopFollowDisplayNameConfig.xcconfig" 23 | local base_target="LoopFollowDisplayNameConfig" 24 | local default_display_name="LoopFollow" 25 | local target_file 26 | local base_target_name 27 | 28 | # Check the value of REPO_NAME and set the target_file accordingly 29 | case $REPO_NAME in 30 | "LoopFollow_Second") 31 | base_target_name="${base_target}_Second" 32 | default_display_name="${default_display_name}_Second" 33 | ;; 34 | "LoopFollow_Third") 35 | base_target_name="${base_target}_Third" 36 | default_display_name="${default_display_name}_Third" 37 | ;; 38 | *) 39 | base_target_name="${base_target}" 40 | ;; 41 | esac 42 | target_file="${BUILD_DIR}/${base_target_name}.xcconfig" 43 | 44 | 45 | section_divider 46 | # Check if the target (display_name) file exists 47 | if [ -f "$target_file" ]; then 48 | # If it exists, remove the file downloaded from repo 49 | # It will be replaced 50 | rm -f "$current_file" 51 | # Report current display name 52 | echo -e "${INFO_FONT}Display name file exists: ${NC}" 53 | else 54 | # Ask user for their display_name preference 55 | echo -e "${INFO_FONT}Display name set to default value of:${NC}" 56 | echo -e "${INFO_FONT} ${default_display_name}${NC}" 57 | echo -e "" 58 | options=("Use Default" "Modify Display Name") 59 | select opt in "${options[@]}" 60 | # If user quits out of script, target not created, available for next attempt 61 | # Move the current file to the target location 62 | do 63 | mv "$current_file" "$target_file" 64 | case $opt in 65 | "Use Default") 66 | break 67 | ;; 68 | "Modify Display Name") 69 | display_name_suggestion 70 | read -p "Enter desired display name to show on Follow app: " looperID 71 | sed -i '' "s|display_name = ${default_display_name}|display_name = ${looperID}|" "$target_file" 72 | break 73 | ;; 74 | esac 75 | done 76 | fi 77 | echo "// The original file has been moved to:" > "$current_file" 78 | echo "// $target_file" >> "$current_file" 79 | echo "// Please edit the display name there." >> "$current_file" 80 | echo -e "" 81 | 82 | # Update Config.xcconfig to point to appropriate display_name file 83 | local config_file="Config.xcconfig" 84 | local include_line="#include? \"$target_file\"" 85 | 86 | # Replace the include line with the new target file 87 | sed -i '' "s|\"\.\./\.\./${base_target}|\"\.\./\.\./${base_target_name}|" "$config_file" 88 | 89 | # report the display name and provide editing instructions 90 | tail -1 "$target_file" 91 | echo "" 92 | echo -e "To modify the display_name, edit this file before you continue:" 93 | echo -e "${target_file}" 94 | display_name_suggestion 95 | return_when_ready 96 | 97 | cd .. 98 | } 99 | 100 | ############################################################ 101 | # End of functions used by LoopFollow build script 102 | ############################################################ 103 | -------------------------------------------------------------------------------- /src/BuildTrio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildTrio.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | # use app_name instead of hard-coded strings 9 | app_name="Trio" 10 | 11 | BUILD_DIR=~/Downloads/"Build${app_name}" 12 | USE_OVERRIDE_IN_REPO="0" 13 | OVERRIDE_FILE="ConfigOverride.xcconfig" 14 | DEV_TEAM_SETTING_NAME="DEVELOPER_TEAM" 15 | 16 | # sub modules are required 17 | CLONE_SUB_MODULES="1" 18 | 19 | # leave this code here, not in use now 20 | FLAG_USE_SHA=0 # Initialize FLAG_USE_SHA to 0 21 | FIXED_SHA="" # Initialize FIXED_SHA with an empty string 22 | 23 | #!inline build_functions.sh 24 | 25 | ############################################################ 26 | # The rest of this is specific to the particular script 27 | ############################################################ 28 | 29 | # Set default values only if they haven't been defined as environment variables 30 | : ${SCRIPT_BRANCH:="main"} 31 | 32 | open_source_warning 33 | 34 | ############################################################ 35 | # Welcome & Branch Selection 36 | ############################################################ 37 | 38 | # when public: 39 | URL_THIS_SCRIPT="https://github.com/nightscout/Trio.git" 40 | URL_FOR_DISCORD="discord.triodocs.org" 41 | URL_FOR_FACEBOOK="facebook.triodocs.org" 42 | URL_FOR_DOCS="triodocs.org" 43 | 44 | # Keep this for when we need a special branch name 45 | # If not used, make this empty string and comment out the menu option 46 | special_branch_name="" 47 | 48 | function select_main() { 49 | #branch_select ${URL_THIS_SCRIPT} main 50 | branch_select ${URL_THIS_SCRIPT} main 51 | } 52 | 53 | function select_dev() { 54 | branch_select ${URL_THIS_SCRIPT} dev 55 | } 56 | 57 | function select_special_branch() { 58 | branch_select ${URL_THIS_SCRIPT} ${special_branch_name} ${app_name}_${special_branch_name} 59 | } 60 | 61 | if [ -z "$CUSTOM_BRANCH" ]; then 62 | while [ -z "$BRANCH" ]; do 63 | section_separator 64 | echo -e "${INFO_FONT}You are running the script to build ${app_name}${NC}" 65 | echo -e "" 66 | echo -e " ${INFO_FONT}Welcome: You can get help for ${app_name} at:${NC}" 67 | echo -e " Facebook : ${URL_FOR_FACEBOOK}" 68 | echo -e " Discord : ${URL_FOR_DISCORD}" 69 | echo -e " Documentation : ${URL_FOR_DOCS}" 70 | echo -e "" 71 | echo -e " ${INFO_FONT}Trio dev is recommended${NC}" 72 | echo -e " ${INFO_FONT}* Check the settings screen frequently${NC}" 73 | echo -e " ${INFO_FONT}* Download/rebuild when latest dev is at a higher version${NC}" 74 | echo -e "" 75 | echo -e " If you insist on building Trio main, you can no longer use this script." 76 | echo -e " Ask a mentor for help." 77 | echo -e "" 78 | echo -e "Before you continue, please ensure" 79 | echo -e " you have Xcode and Xcode command line tools installed\n" 80 | 81 | options=(\ 82 | #"${app_name} main" \ 83 | "${app_name} dev" \ 84 | # "${app_name} ${special_branch_name}" \ 85 | "$(exit_or_return_menu)") 86 | actions=(\ 87 | #"select_main" \ 88 | "select_dev" \ 89 | # "select_special_branch" \ 90 | "exit_script") 91 | menu_select "${options[@]}" "${actions[@]}" 92 | done 93 | else 94 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 95 | fi 96 | 97 | ############################################################ 98 | # Standard Build train 99 | ############################################################ 100 | 101 | verify_xcode_path 102 | check_versions 103 | clone_repo 104 | automated_clone_download_error_check 105 | 106 | # special build train for lightly tested commit 107 | cd $REPO_NAME 108 | 109 | this_dir="$(pwd)" 110 | echo -e "In ${this_dir}" 111 | if [ ${FLAG_USE_SHA} == 1 ]; then 112 | echo -e " Checking out commit ${FIXED_SHA}\n" 113 | git checkout ${FIXED_SHA} --recurse-submodules --quiet 114 | git describe --tags --exact-match 115 | git rev-parse HEAD 116 | echo -e "Continue if no errors reported" 117 | return_when_ready 118 | fi 119 | 120 | check_config_override_existence_offer_to_configure 121 | ensure_a_year 122 | 123 | ############################################################ 124 | # Open Xcode 125 | ############################################################ 126 | 127 | section_divider 128 | before_final_return_message 129 | echo -e "" 130 | return_when_ready 131 | xed . 132 | after_final_return_message 133 | exit_script 134 | -------------------------------------------------------------------------------- /src/CustomizationSelect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script CustomizationSelect.sh 2 | 3 | BUILD_DIR=~/Downloads/BuildLoop 4 | 5 | # Customization parameters for Loop 6 | app_name="Loop" 7 | app_folder_name="LoopWorkspace" 8 | 9 | #!inline patch_functions.sh 10 | 11 | ############################################################ 12 | # The rest of this is specific to the particular script 13 | ############################################################ 14 | 15 | message_incompatible_count=0 16 | 17 | # this is always used - it is the introductory message - it can be blank 18 | # it comes before any customizations are presented 19 | function message_generic() { 20 | echo " These Customizations are documented on the Loop and Learn web site" 21 | echo " https://www.loopandlearn.org/custom-code#custom-list" 22 | echo 23 | echo " These customizations are valid for Loop 3.6" 24 | echo 25 | } 26 | 27 | # this is always used - it is the incompatible patches message - it can be blank 28 | function message_incompatible() { 29 | : 30 | } 31 | 32 | # This section contains optional messages used for some of the customization lines 33 | # In order for optional messages to appear when added to the add_customization line 34 | # must use printf 35 | 36 | function message_to_add_blank_line() { 37 | printf "\n" 38 | } 39 | 40 | function message_for_profiles() { 41 | printf " This feature enables save and restore of named profiles\n" 42 | printf " https://www.loopandlearn.org/loop-features-in-development#profiles\n" 43 | } 44 | 45 | function message_for_basal_lock() { 46 | printf " Enables override of Loop behavior for high glucose\n" 47 | printf " https://www.loopandlearn.org/loop-features-in-development#basal-lock\n\n" 48 | } 49 | 50 | function message_for_live_activity() { 51 | printf " ${INFO_FONT}Xcode MUST be closed${NC}\n" 52 | printf " Add Live Activity and Dynamic Island\n" 53 | printf " Requires iPhone 14 or newer; iOS 16.2 or newer\n" 54 | printf " https://www.loopandlearn.org/loop-features-in-development#live-activity\n\n" 55 | } 56 | 57 | function message_for_negative_insulin() { 58 | printf " Add a new model that mitigates accumulated negative insulin\n" 59 | printf " https://www.loopandlearn.org/loop-features-in-development#negative-insulin\n\n" 60 | } 61 | 62 | function message_for_remote_window() { 63 | printf " Increase the time-out for LoopCaregiver remote command OTP to 15 minutes\n" 64 | printf " https://www.loopandlearn.org/loop-features-in-development#remote-window\n\n" 65 | } 66 | 67 | # list patches in this order with args: 68 | # User facing information for option 69 | # Folder name in the patch repo 70 | # (Optional) message function shown prior to option 71 | 72 | add_customization "Change Default to Upload Dexcom Readings" "dexcom_upload_readings" 73 | add_customization "Increase Future Carbs Limit to 4 hours" "future_carbs_4h" 74 | add_customization "Modify Carb Warning & Limit: Low Carb to 49 & 99" "low_carb_limit" 75 | add_customization "Modify Carb Warning & Limit: High Carb to 201 & 300" "high_carb_limit" 76 | add_customization "Disable Authentication Requirement" "no_auth" "message_to_add_blank_line" 77 | 78 | add_customization "Override Insulin Needs Picker (50% to 200%, steps of 5%)" "override_sens" 79 | add_customization "Add now line to charts" "now_line" 80 | add_customization "Modify Logo to include LnL icon" "lnl_icon" 81 | add_customization "Remove Loop Title on Watch App" "watch_title" 82 | add_customization "2 hour Absorption Time for Lollipop" "2hlollipop" "message_to_add_blank_line" 83 | 84 | add_customization "Display 2 Days of Meal History" "meal_days" 85 | add_customization "Display a Week of Meal History (Slow after Restart)" "meal_week" "message_to_add_blank_line" 86 | 87 | add_customization "Profile Save & Load" "profiles" "message_for_profiles" 88 | add_customization "Basal Lock" "basal_lock" "message_for_basal_lock" "1" 89 | # live_activity changes the minimum iOS allowed and therefore requires xcode to be closed 90 | add_customization "Live Activity/Dynamic Island" "live_activity" "message_for_live_activity" "1" "Verify that Xcode is closed before continuing!" 91 | add_customization "Negative Insulin Damper" "negative_insulin" "message_for_negative_insulin" 92 | 93 | add_customization "Increase Remote Window to 15 minutes" "remote_window" "message_for_remote_window" 94 | 95 | add_translation "2002" "profiles" 96 | 97 | param_zero_is_customization 98 | param_zero_result=$? 99 | 100 | if [ $param_zero_result -eq 0 ]; then 101 | patch_command_line $0 "$@" 102 | elif [ $# -gt 0 ] && [ -n "$1" ]; then 103 | patch_command_line "$@" 104 | else 105 | if [ "$GITHUB_ACTIONS" != "true" ]; then 106 | patch_menu 107 | else 108 | echo -e "${ERROR_FONT} Customization in Browser Build executed without parameters, check that there is no empty line after CustomizationSelect.sh.{NC}" 109 | exit 1 110 | fi 111 | fi -------------------------------------------------------------------------------- /src/Build_iAPS.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script Build_iAPS.sh 2 | 3 | ############################################################ 4 | # Required parameters for any build script that uses 5 | # inline build_functions 6 | ############################################################ 7 | 8 | BUILD_DIR=~/Downloads/"Build_iAPS" 9 | # For iAPS, OVERRIDE_FILE is inside newly downloaded iAPS folder 10 | USE_OVERRIDE_IN_REPO="1" 11 | OVERRIDE_FILE="ConfigOverride.xcconfig" 12 | DEV_TEAM_SETTING_NAME="DEVELOPER_TEAM" 13 | 14 | # sub modules are not required 15 | CLONE_SUB_MODULES="0" 16 | 17 | FLAG_USE_SHA=0 # Initialize FLAG_USE_SHA to 0 18 | FIXED_SHA="" # Initialize FIXED_SHA with an empty string 19 | 20 | #!inline build_functions.sh 21 | #!inline utility_scripts.sh 22 | #!inline run_script.sh 23 | 24 | ############################################################ 25 | # The rest of this is specific to the particular script 26 | ############################################################ 27 | 28 | # Set default values only if they haven't been defined as environment variables 29 | : ${SCRIPT_BRANCH:="main"} 30 | 31 | 32 | ############################################################ 33 | # Welcome & Branch Selection 34 | ############################################################ 35 | 36 | URL_THIS_SCRIPT="https://github.com/Artificial-Pancreas/iAPS.git" 37 | 38 | # Trio information 39 | URL_FOR_TRIO_DISCORD="discord.gg/FnwFEFUwXE" 40 | URL_FOR_TRIO_FACEBOOK="facebook.com/groups/diytrio" 41 | URL_FOR_TRIO_WEBSITE="diy-trio.org" 42 | URL_FOR_TRIO_DOCS="triodocs.org" 43 | 44 | function select_iaps_main() { 45 | branch_select ${URL_THIS_SCRIPT} main 46 | } 47 | 48 | function select_iaps_dev() { 49 | branch_select ${URL_THIS_SCRIPT} dev 50 | } 51 | 52 | section_separator 53 | 54 | echo -e "\n ${INFO_FONT}You are running the Build_iAPS script${NC}" 55 | echo -e "" 56 | echo -e "Access to this script is available as a courtesy;" 57 | echo -e " it might be removed without notice." 58 | echo -e "" 59 | echo -e "The Loop and Learn team that created this script" 60 | echo -e " suggest you build ${INFO_FONT}Trio${NC} instead." 61 | echo -e "" 62 | echo -e "Please read about this alternative here:" 63 | echo -e "" 64 | echo -e " Trio Website link: ${URL_FOR_TRIO_WEBSITE}" 65 | echo -e " Trio Facebook link: ${URL_FOR_TRIO_FACEBOOK}" 66 | echo -e " Trio Discord link: ${URL_FOR_TRIO_DISCORD}" 67 | echo -e "" 68 | section_divider 69 | 70 | options=(\ 71 | "Switch to the TrioBuildSelectScript for Trio (New App, New App Group)" \ 72 | "Continue with the iAPS script" \ 73 | "Exit the script") 74 | actions=(\ 75 | "WHICH=Trio" \ 76 | "WHICH=iAPS" \ 77 | "exit_script") 78 | menu_select "${options[@]}" "${actions[@]}" 79 | 80 | if [ "$WHICH" = "Trio" ]; then 81 | run_script "TrioBuildSelectScript.sh" 82 | exit_script 83 | fi 84 | 85 | open_source_warning 86 | 87 | if [ -z "$CUSTOM_BRANCH" ]; then 88 | while [ -z "$BRANCH" ]; do 89 | section_separator 90 | echo -e "\n ${INFO_FONT}You are running the script to build iAPS${NC}" 91 | echo -e " ${INFO_FONT} or run maintenance utilities${NC}" 92 | echo -e "" 93 | echo -e "Before you continue, please ensure" 94 | echo -e " you have Xcode and Xcode command line tools installed\n" 95 | echo -e "Please select which branch of iAPS to download and build." 96 | echo -e "" 97 | echo -e "Documentation for iAPS:" 98 | echo -e " https://iaps.readthedocs.io/en/main/" 99 | echo -e "Documentation for maintenance utilities:" 100 | echo -e " https://www.loopandlearn.org/build-select/#utilities-disk" 101 | echo -e "" 102 | 103 | options=("iAPS main" "iAPS dev" "Run Maintenance Utilities" "$(exit_or_return_menu)") 104 | actions=("select_iaps_main" "select_iaps_dev" "utility_scripts" "exit_script") 105 | menu_select "${options[@]}" "${actions[@]}" 106 | done 107 | else 108 | branch_select ${URL_THIS_SCRIPT} $CUSTOM_BRANCH 109 | fi 110 | 111 | ############################################################ 112 | # Standard Build train 113 | ############################################################ 114 | 115 | verify_xcode_path 116 | check_versions 117 | clone_repo 118 | automated_clone_download_error_check 119 | 120 | # special build train for lightly tested commit 121 | cd $REPO_NAME 122 | 123 | this_dir="$(pwd)" 124 | echo -e "In ${this_dir}" 125 | if [ ${FLAG_USE_SHA} == 1 ]; then 126 | echo -e " Checking out commit ${FIXED_SHA}\n" 127 | git checkout ${FIXED_SHA} --recurse-submodules --quiet 128 | git describe --tags --exact-match 129 | git rev-parse HEAD 130 | echo -e "Continue if no errors reported" 131 | return_when_ready 132 | fi 133 | 134 | check_config_override_existence_offer_to_configure 135 | ensure_a_year 136 | 137 | ############################################################ 138 | # Open Xcode 139 | ############################################################ 140 | 141 | section_divider 142 | before_final_return_message 143 | echo -e "" 144 | return_when_ready 145 | xed . 146 | after_final_return_message 147 | exit_script 148 | -------------------------------------------------------------------------------- /inline_functions/common.sh: -------------------------------------------------------------------------------- 1 | STARTING_DIR="${PWD}" 2 | 3 | ############################################################ 4 | # define some font styles and colors 5 | ############################################################ 6 | 7 | # remove special font 8 | NC='\033[0m' 9 | # add special font 10 | #INFO_FONT='\033[1;36m' 11 | INFO_FONT='\033[1m' 12 | SUCCESS_FONT='\033[1;32m' 13 | ERROR_FONT='\033[1;31m' 14 | 15 | function section_divider() { 16 | echo -e "" 17 | echo -e "--------------------------------" 18 | echo -e "" 19 | } 20 | 21 | function section_separator() { 22 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 23 | echo -e "\033[2J\033[H" 2>/dev/null 24 | section_divider 25 | } 26 | 27 | function return_when_ready() { 28 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 29 | read -p "" dummy 30 | } 31 | 32 | # Skip if this script is called from another script, then this has already been displayed 33 | if [ "$0" != "_" ]; then 34 | # Inform the user about env variables set 35 | # Variables definition 36 | variables=( 37 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 38 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 39 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 40 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 41 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 42 | "CUSTOM_URL: Overrides the repo url." 43 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 44 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 45 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 46 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 47 | "PATCH_BRANCH: Indicates the source branch for patches." 48 | "PATCH_REPO: Specifies the URL of the patch source repository." 49 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 50 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 51 | ) 52 | 53 | # Flag to check if any variable is set 54 | any_variable_set=false 55 | 56 | # Iterate over each variable 57 | for var in "${variables[@]}"; do 58 | # Split the variable name and description 59 | IFS=":" read -r name description <<<"$var" 60 | 61 | # Check if the variable is set 62 | if [ -n "${!name}" ]; then 63 | # If this is the first variable set, print the initial message 64 | if ! $any_variable_set; then 65 | section_separator 66 | echo -e "For your information, you are running this script in customized mode" 67 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 68 | echo -e "Developers might have additional environment variables set:" 69 | any_variable_set=true 70 | fi 71 | 72 | # Print the variable name, value, and description 73 | echo " - $name: ${!name}" 74 | echo " $description" 75 | fi 76 | done 77 | if $any_variable_set; then 78 | echo 79 | echo "To clear the values, close this terminal and start a new one." 80 | echo "Sleeping for 2 sec then continuing" 81 | sleep 2 82 | fi 83 | fi 84 | 85 | function choose_option() { 86 | echo -e "Type a number from the list below and return to proceed." 87 | section_divider 88 | } 89 | 90 | function invalid_entry() { 91 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 92 | } 93 | 94 | function do_continue() { 95 | : 96 | } 97 | 98 | function menu_select() { 99 | choose_option 100 | 101 | local options=("${@:1:$#/2}") 102 | local actions=("${@:$(($# + 1))/2+1}") 103 | 104 | while true; do 105 | select opt in "${options[@]}"; do 106 | for i in $(seq 0 $((${#options[@]} - 1))); do 107 | if [ "$opt" = "${options[$i]}" ]; then 108 | eval "${actions[$i]}" 109 | return 110 | fi 111 | done 112 | invalid_entry 113 | break 114 | done 115 | done 116 | } 117 | 118 | function exit_or_return_menu() { 119 | if [ "$0" != "_" ]; then 120 | # Called directly 121 | echo "Exit Script" 122 | else 123 | # Called from BuildSelectScript 124 | echo "Return to Menu" 125 | fi 126 | } 127 | 128 | function exit_script() { 129 | if [ "$0" != "_" ]; then 130 | # Called directly 131 | exit_message 132 | else 133 | # Called from BuildSelectScript 134 | exit 0 135 | fi 136 | } 137 | 138 | function exit_message() { 139 | section_divider 140 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 141 | echo -e " You may close the terminal" 142 | echo -e "or" 143 | echo -e " You can press the up arrow ⬆️ on the keyboard" 144 | echo -e " and return to repeat script from beginning" 145 | section_divider 146 | exit 0 147 | } 148 | 149 | function erase_previous_line { 150 | if [ -n "$TERM" ]; then 151 | (tput cuu1 && tput el) 2>/dev/null || true 152 | fi 153 | } -------------------------------------------------------------------------------- /CleanDerived.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | section_separator 167 | echo -e "${INFO_FONT}If you did not quit Xcode before selecting, you might see errors${NC}" 168 | echo -e "\n\n🕒 Please be patient. On older computers and virtual machines, this may take 5-10 minutes or longer to run.\n" 169 | echo -e "✅ Cleaning Derived Data files.\n" 170 | rm -rf ~/Library/Developer/Xcode/DerivedData 171 | echo -e "✅ Done Cleaning" 172 | echo -e " If Xcode was open, you may see a 'Permission denied' statement." 173 | echo -e " In that case, quit out of Xcode and run the script again\n" 174 | exit_script 175 | # *** End of inlined file: src/CleanDerived.sh *** 176 | 177 | -------------------------------------------------------------------------------- /inline_functions/delete_old_downloads.sh: -------------------------------------------------------------------------------- 1 | # Flag to skip all deletions 2 | SKIP_ALL=false 3 | folder_count=0 4 | app_pattern_count=0 5 | 6 | # Default if environment variable is not set 7 | : ${DELETE_SELECTED_FOLDERS:="1"} 8 | 9 | function list_build_folders_when_testing() { 10 | # only echo pattern when testing 11 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 12 | echo 13 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 14 | echo -e " This is the list of all patterns that will be searched${NC}" 15 | echo 16 | for pattern in "${patterns[@]}"; do 17 | echo " $pattern" 18 | done 19 | section_divider 20 | fi 21 | } 22 | 23 | function delete_folders_except_latest() { 24 | local pattern="$1" 25 | local total_size=0 26 | local unsorted_folders=() 27 | 28 | # First loop for case-sensitive matching 29 | for entry in ~/Downloads/$pattern; do 30 | [ -d "$entry" ] && unsorted_folders+=("$entry") 31 | done 32 | 33 | # Second loop for case-insensitive matching, but only if "main" is in the pattern 34 | if [[ $pattern == *main* ]]; then 35 | for entry in ~/Downloads/${pattern//main/Main}; do 36 | [ -d "$entry" ] && unsorted_folders+=("$entry") 37 | done 38 | fi 39 | 40 | # Sort the folders array by date (newest first) 41 | IFS=$'\n' folders=($(sort -r <<<"${unsorted_folders[*]}")) 42 | IFS=$' \t\n' # Reset IFS to default value. 43 | 44 | if [ ${#folders[@]} -eq 0 ]; then 45 | return 46 | fi 47 | 48 | # increment because folders were found 49 | ((app_pattern_count=app_pattern_count+1)) 50 | 51 | if [ ${#folders[@]} -eq 1 ]; then 52 | echo "Only one download found for app pattern: '$pattern'" 53 | return 54 | fi 55 | 56 | section_divider 57 | 58 | echo "More than one download found for app pattern:" 59 | echo " '$pattern':" 60 | echo 61 | echo "Download Folder to Keep:" 62 | echo " ${folders[0]/#$HOME/~}" 63 | echo 64 | 65 | echo "Download Folder(s) that can be deleted:" 66 | 67 | for folder in "${folders[@]:1}"; do 68 | echo " ${folder/#$HOME/~}" 69 | total_size=$(($total_size + $(du -s "$folder" | awk '{print $1}'))) 70 | done 71 | 72 | echo 73 | echo -e " If Xcode is open in a folder you plan to delete," 74 | echo -e " ${INFO_FONT}Quit Xcode${NC} before deleting" 75 | 76 | total_size_mb=$(echo "scale=2; $total_size / 1024" | bc) 77 | echo 78 | echo "Total size to be deleted: $total_size_mb MB" 79 | section_divider 80 | 81 | options=( 82 | "Delete these Folders" 83 | "Skip delete at this location" 84 | "$(exit_or_return_menu)") 85 | actions=( 86 | "delete_selected_folders \"$pattern\"" 87 | "return" 88 | "exit_script") 89 | menu_select "${options[@]}" "${actions[@]}" 90 | } 91 | 92 | function delete_selected_folders() { 93 | local pattern="$1" 94 | local unsorted_folders=() 95 | 96 | # First loop for case-sensitive matching 97 | for entry in ~/Downloads/$pattern; do 98 | [ -d "$entry" ] && unsorted_folders+=("$entry") 99 | done 100 | 101 | # Second loop for case-insensitive matching, but only if "main" is in the pattern 102 | if [[ $pattern == *main* ]]; then 103 | for entry in ~/Downloads/${pattern//main/Main}; do 104 | [ -d "$entry" ] && unsorted_folders+=("$entry") 105 | done 106 | fi 107 | 108 | # Sort the folders array by date (newest first) 109 | IFS=$'\n' folders=($(sort -r <<<"${unsorted_folders[*]}")) 110 | IFS=$' \t\n' # Reset IFS to default value. 111 | echo 112 | 113 | this_pattern_count=0 114 | 115 | for folder in "${folders[@]:1}"; do 116 | if [ ${DELETE_SELECTED_FOLDERS} == 1 ]; then 117 | rm -rf "$folder" 118 | fi 119 | echo " Removed $folder" 120 | ((folder_count=folder_count+1)) 121 | ((this_pattern_count=this_pattern_count+1)) 122 | done 123 | 124 | echo -e "✅ ${SUCCESS_FONT}Deleted ${this_pattern_count} download folders for this app pattern${NC}" 125 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 126 | echo 127 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 128 | echo -e " So folders marked successfully deleted are still there${NC}" 129 | fi 130 | echo 131 | return_when_ready 132 | } 133 | 134 | function skip_all() { 135 | SKIP_ALL=true 136 | } 137 | 138 | function delete_old_downloads() { 139 | patterns=( 140 | "BuildLoop/Loop-*" 141 | "BuildLoop/Loop_lnl_patches-*" 142 | "BuildLoop/LoopCaregiver_dev-*" 143 | "BuildLoop/LoopWorkspace_*" 144 | "BuildLoop/Loop_dev-*" 145 | "BuildLoop/FreeAPS*" 146 | "BuildLoopFollow/LoopFollow_main*" 147 | "BuildLoopFollow/LoopFollow_Second_main*" 148 | "BuildLoopFollow/LoopFollow_Third_main*" 149 | "BuildLoopFollow/LoopFollow_dev*" 150 | "BuildxDrip4iOS/xDrip4iOS*" 151 | "Build_iAPS/iAPS_main_*" 152 | "Build_iAPS/iAPS_main-*" 153 | "Build_iAPS/iAPS_dev*" 154 | "BuildTrio/Trio_main*" 155 | "BuildTrio/Trio_dev*" 156 | ) 157 | 158 | list_build_folders_when_testing 159 | 160 | if [ "$SKIP_ALL" = false ] ; then 161 | section_divider 162 | echo "For each type of Build provided as a build script, " 163 | echo " you will be shown your most recent download" 164 | echo " and given the option to remove older downloads." 165 | 166 | for pattern in "${patterns[@]}"; do 167 | if [ "$SKIP_ALL" = false ] ; then 168 | delete_folders_except_latest "$pattern" 169 | else 170 | break 171 | fi 172 | done 173 | fi 174 | 175 | echo 176 | echo -e "✅ ${SUCCESS_FONT}Download folders have been examined for all app patterns.${NC}" 177 | echo -e " There were ${app_pattern_count} app patterns that contain one or more download" 178 | if [ ${folder_count} -eq 0 ]; then 179 | echo -e " No Download folders deleted" 180 | else 181 | echo -e " Deleted a total of ${folder_count} older download folders" 182 | fi 183 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 184 | echo 185 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 186 | echo -e " So folders marked successfully deleted are still there${NC}" 187 | fi 188 | } 189 | -------------------------------------------------------------------------------- /XcodeClean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | section_separator 167 | echo -e "${INFO_FONT}If you did not quit Xcode before selecting, this might not clean everything${NC}" 168 | 169 | echo -e "\n\n🕒 Please be patient. On older computers and virtual machines, this may take 5-10 minutes or longer to run.\n" 170 | 171 | echo -e "\n\n✅ Removing Developer iOS DeviceSupport Library\n" 172 | rm -rf "$HOME/Library/Developer/Xcode/iOS\ DeviceSupport" 173 | 174 | echo -e "✅ Removing Developer watchOS DeviceSupport Library\n" 175 | rm -rf "$HOME/Library/Developer/Xcode/watchOS\ DeviceSupport" 176 | 177 | echo -e "✅ Removing Developer DerivedData\n" 178 | rm -rf "$HOME/Library/Developer/Xcode/DerivedData" 179 | 180 | echo -e " If Xcode was open, you may see a 'Permission denied' statement." 181 | echo -e " In that case, quit out of Xcode and run the script again before rebooting\n" 182 | 183 | echo -e "🛑 Please Reboot Now\n\n"; 184 | exit_script 185 | # *** End of inlined file: src/XcodeClean.sh *** 186 | 187 | -------------------------------------------------------------------------------- /CleanProfiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | 167 | # *** Start of inlined file: inline_functions/clean_profiles.sh *** 168 | ############################################################ 169 | # clean_profiles function 170 | # Action: deletes saved mobileprovisions from Mac 171 | # Information: 172 | # If Xcode is open, *.mobileprovisions are deleted and new ones generated 173 | # The path changed between Xcode 15 and Xcode 16, delete both folders 174 | ############################################################ 175 | 176 | clean_profiles() { 177 | xcode15_path=${HOME}/Library/MobileDevice/Provisioning\ Profiles 178 | xcode16_path=${HOME}/Library/Developer/Xcode/UserData/Provisioning\ Profiles 179 | 180 | echo -e "\n✅ Cleaning Profiles" 181 | echo -e " This ensures the next app you build with Xcode will last a year." 182 | if [[ -d "$xcode15_path" ]]; then 183 | rm -rf "$xcode15_path" 184 | fi 185 | if [[ -d "$xcode16_path" ]]; then 186 | rm -rf "$xcode16_path" 187 | fi 188 | echo -e "✅ Profiles are cleaned." 189 | } 190 | # *** End of inlined file: inline_functions/clean_profiles.sh *** 191 | 192 | 193 | section_separator 194 | clean_profiles 195 | exit_script 196 | # *** End of inlined file: src/CleanProfiles.sh *** 197 | 198 | -------------------------------------------------------------------------------- /inline_functions/building_config_override.sh: -------------------------------------------------------------------------------- 1 | function check_config_override_existence_offer_to_configure() { 2 | section_separator 3 | 4 | # Automatic signing functionality: 5 | # 1) Use existing Override file 6 | # 2) Copy team from latest provisioning profile 7 | # 3) Enter team manually with option to skip 8 | 9 | # Options for USE_OVERRIDE_IN_REPO 10 | # 0 means copy file in repo up 2 levels and use that 11 | # 1 create the file in the repo and add development team 12 | # 2 create the file in the repo with extra line(s) and the team 13 | if [[ $USE_OVERRIDE_IN_REPO -ge 1 ]]; then 14 | OVERRIDE_FULLPATH="${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" 15 | else 16 | OVERRIDE_FULLPATH="${BUILD_DIR}/${OVERRIDE_FILE}" 17 | fi 18 | 19 | if [ -f ${OVERRIDE_FULLPATH} ] && \ 20 | grep -q "^$DEV_TEAM_SETTING_NAME" ${OVERRIDE_FULLPATH}; then 21 | # how_to_find_your_id 22 | report_persistent_config_override 23 | else 24 | PROFILES_DIR="$HOME/Library/MobileDevice/Provisioning Profiles" 25 | 26 | if [ -d "${PROFILES_DIR}" ]; then 27 | latest_file=$(find "${PROFILES_DIR}" -type f -name "*.mobileprovision" -print0 | xargs -0 ls -t | head -n1) 28 | if [ -n "$latest_file" ]; then 29 | # Decode the .mobileprovision file using the security command 30 | decoded_xml=$(security cms -D -i "$latest_file") 31 | 32 | # Extract the Team ID from the XML 33 | DEVELOPMENT_TEAM=$(echo "$decoded_xml" | awk -F'[<>]' '/TeamIdentifier<\/key>/ { getline; getline; print $3 }') 34 | fi 35 | fi 36 | 37 | if [ -n "$DEVELOPMENT_TEAM" ]; then 38 | echo -e "Using TeamIdentifier from the latest provisioning profile\n" 39 | set_development_team "$DEVELOPMENT_TEAM" 40 | report_persistent_config_override 41 | else 42 | echo -e "Choose 1 to Sign Automatically or " 43 | echo -e " 2 to Sign Manually (later in Xcode)" 44 | echo -e "\nIf you choose Sign Automatically, script guides you" 45 | echo -e " to create a permanent signing file" 46 | echo -e " containing your Apple Developer ID" 47 | choose_option 48 | options=("Sign Automatically" "Sign Manually" "$(exit_or_return_menu)") 49 | select opt in "${options[@]}" 50 | do 51 | case $opt in 52 | "Sign Automatically") 53 | create_persistent_config_override 54 | break 55 | ;; 56 | "Sign Manually") 57 | break 58 | ;; 59 | "$(exit_or_return_menu)") 60 | exit_script 61 | ;; 62 | *) # Invalid option 63 | invalid_entry 64 | ;; 65 | esac 66 | done 67 | fi 68 | fi 69 | } 70 | 71 | function report_persistent_config_override() { 72 | echo -e "Your Apple Developer ID was found automatically:" 73 | grep "^$DEV_TEAM_SETTING_NAME" ${OVERRIDE_FULLPATH} 74 | echo -e "\nIf that is correct your app will be automatically signed\n" 75 | options=("ID is OK" "Editing Instructions" "$(exit_or_return_menu)") 76 | select opt in "${options[@]}" 77 | do 78 | case $opt in 79 | "ID is OK") 80 | break 81 | ;; 82 | "Editing Instructions") 83 | section_divider 84 | echo -e " Part 1: How to find your Apple Developer ID" 85 | echo -e "" 86 | how_to_find_your_id 87 | echo -e "" 88 | echo -e " Part 2: Edit the automatic signing file before hitting return" 89 | echo -e " step 1: open finder, " 90 | echo -e " step 2: locate and double click on" 91 | echo -e " ${OVERRIDE_FULLPATH/$HOME/~}" 92 | echo -e " to open that file in Xcode" 93 | echo -e " step 3: find the line that starts with " 94 | echo -e " ${DEV_TEAM_SETTING_NAME}=" 95 | echo -e " and modify the value to be your " 96 | echo -e " Apple Developer ID" 97 | echo -e " step 4: save the file" 98 | echo -e "" 99 | echo -e " When ready to proceed, hit return" 100 | return_when_ready 101 | break 102 | ;; 103 | "$(exit_or_return_menu)") 104 | exit_script 105 | ;; 106 | *) # Invalid option 107 | invalid_entry 108 | ;; 109 | esac 110 | done 111 | } 112 | 113 | function how_to_find_your_id() { 114 | echo -e "Your Apple Developer ID is the 10-character Team ID" 115 | echo -e " found on the Membership page after logging into your account at:" 116 | echo -e " https://developer.apple.com/account/#!/membership\n" 117 | echo -e "It may be necessary to click on the Membership Details icon" 118 | } 119 | 120 | function create_persistent_config_override() { 121 | section_separator 122 | echo -e "The Apple Developer page will open when you hit return\n" 123 | how_to_find_your_id 124 | echo -e "That page will be opened for you." 125 | echo -e " Once you get your ID, you will enter it in this terminal window" 126 | return_when_ready 127 | # 128 | open "https://developer.apple.com/account/#!/membership" 129 | echo -e "\n *** \nClick in terminal window so you can" 130 | read -p "Enter the ID and return: " devID 131 | # 132 | section_separator 133 | if [ ${#devID} -ne 10 ]; then 134 | echo -e "Something was wrong with the entry" 135 | echo -e "You can manually sign each target in Xcode" 136 | else 137 | echo -e "Creating ${OVERRIDE_FULLPATH}" 138 | echo -e " with your Apple Developer ID\n" 139 | # For Loop, copy the file and add developer ID 140 | # For other apps, create file with developer ID 141 | set_development_team $devID 142 | report_persistent_config_override 143 | echo -e "\nXcode uses the permanent file to automatically sign your targets" 144 | fi 145 | } 146 | 147 | set_development_team() { 148 | team_id="$1" 149 | if [[ $USE_OVERRIDE_IN_REPO == "0" ]] && 150 | [[ -f "${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" ]]; then 151 | cp -p "${LOCAL_DIR}/$REPO_NAME/${OVERRIDE_FILE}" "${OVERRIDE_FULLPATH}" 152 | elif [[ $USE_OVERRIDE_IN_REPO == "1" ]] || \ 153 | [[ $USE_OVERRIDE_IN_REPO == "2" ]]; then 154 | echo "// Automatic Signing File" > ${OVERRIDE_FULLPATH} 155 | fi 156 | if [[ $USE_OVERRIDE_IN_REPO == "2" ]]; then 157 | for str in ${ADDED_LINE_FOR_OVERRIDE[@]}; do 158 | echo "$str" >> ${OVERRIDE_FULLPATH} 159 | done 160 | fi 161 | echo "$DEV_TEAM_SETTING_NAME = $team_id" >> ${OVERRIDE_FULLPATH} 162 | } 163 | 164 | -------------------------------------------------------------------------------- /patch/cage.patch: -------------------------------------------------------------------------------- 1 | Submodule OmniBLE contains modified content 2 | diff --git a/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift b/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift 3 | index af0516f..257ccaf 100644 4 | --- a/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift 5 | +++ b/OmniBLE/OmniBLE/PumpManager/OmniBLEPumpManager.swift 6 | @@ -874,6 +874,56 @@ extension OmniBLEPumpManager { 7 | #endif 8 | } 9 | 10 | + private func SiteChange() 11 | + { 12 | + do { // Silence all errors and return 13 | + let keychain = KeychainManager() 14 | + let credentials = try keychain.getInternetCredentials(account: "NightscoutAPI") 15 | + 16 | + let date = Date() 17 | + let formatter = ISO8601DateFormatter() 18 | + formatter.timeZone = TimeZone(abbreviation: "UTC") 19 | + let dateString = formatter.string(from: date) 20 | + 21 | + let json: [String: Any] = [ 22 | + "enteredBy": "Loop", 23 | + "timestamp": dateString, 24 | + "eventType": "Site Change", 25 | + "secret": credentials.password.sha1() 26 | + ] 27 | + 28 | + guard var urlComponents = URLComponents(url: credentials.url, resolvingAgainstBaseURL: false) else {return } 29 | + if urlComponents.path.hasSuffix("/") { 30 | + urlComponents.path = String(urlComponents.path.dropLast()) 31 | + } 32 | + urlComponents.path += "/api/v1/treatments.json" 33 | + 34 | + guard let apiURL = urlComponents.url else { return } 35 | + 36 | + var request = URLRequest(url: apiURL) 37 | + request.httpMethod = "POST" 38 | + request.setValue("application/json", forHTTPHeaderField: "Content-Type") 39 | + request.httpBody = try? JSONSerialization.data(withJSONObject: json) 40 | + 41 | + let task = URLSession.shared.dataTask(with: request) { data, response, error in 42 | + if let error = error { 43 | + // Handle network error 44 | + print("Error: \(error)") 45 | + return 46 | + } 47 | + 48 | + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { 49 | + // Handle server error 50 | + print("Error: Invalid response") 51 | + return 52 | + } 53 | + } 54 | + task.resume() 55 | + } catch { 56 | + return 57 | + } 58 | + } 59 | + 60 | // Called on the main thread 61 | public func insertCannula(completion: @escaping (Result) -> Void) { 62 | 63 | @@ -894,6 +944,7 @@ extension OmniBLEPumpManager { 64 | var podState = state.podState 65 | podState?.setupProgress = .completed 66 | state.updatePodStateFromPodComms(podState) 67 | + self.SiteChange() 68 | return .success(mockDelay) 69 | }) 70 | 71 | @@ -944,6 +995,7 @@ extension OmniBLEPumpManager { 72 | ] 73 | 74 | let finishWait = try session.insertCannula(optionalAlerts: alerts) 75 | + self.SiteChange() 76 | completion(.success(finishWait)) 77 | } catch let error { 78 | completion(.failure(.communication(error))) 79 | Submodule OmniKit contains modified content 80 | diff --git a/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift b/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift 81 | index f3082f9..a6d39f5 100644 82 | --- a/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift 83 | +++ b/OmniKit/OmniKit/PumpManager/OmnipodPumpManager.swift 84 | @@ -12,7 +12,19 @@ import RileyLinkKit 85 | import RileyLinkBLEKit 86 | import UserNotifications 87 | import os.log 88 | +import CommonCrypto 89 | 90 | +extension String { 91 | + func sha1() -> String { 92 | + let data = Data(self.utf8) 93 | + var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) 94 | + data.withUnsafeBytes { 95 | + _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest) 96 | + } 97 | + let hexBytes = digest.map { String(format: "%02hhx", $0) } 98 | + return hexBytes.joined() 99 | + } 100 | +} 101 | 102 | public enum ReservoirAlertState { 103 | case ok 104 | @@ -813,6 +825,56 @@ extension OmnipodPumpManager { 105 | } 106 | #endif 107 | } 108 | + 109 | + private func SiteChange() 110 | + { 111 | + do { // Silence all errors and return 112 | + let keychain = KeychainManager() 113 | + let credentials = try keychain.getInternetCredentials(account: "NightscoutAPI") 114 | + 115 | + let date = Date() 116 | + let formatter = ISO8601DateFormatter() 117 | + formatter.timeZone = TimeZone(abbreviation: "UTC") 118 | + let dateString = formatter.string(from: date) 119 | + 120 | + let json: [String: Any] = [ 121 | + "enteredBy": "Loop", 122 | + "timestamp": dateString, 123 | + "eventType": "Site Change", 124 | + "secret": credentials.password.sha1() 125 | + ] 126 | + 127 | + guard var urlComponents = URLComponents(url: credentials.url, resolvingAgainstBaseURL: false) else {return } 128 | + if urlComponents.path.hasSuffix("/") { 129 | + urlComponents.path = String(urlComponents.path.dropLast()) 130 | + } 131 | + urlComponents.path += "/api/v1/treatments.json" 132 | + 133 | + guard let apiURL = urlComponents.url else { return } 134 | + 135 | + var request = URLRequest(url: apiURL) 136 | + request.httpMethod = "POST" 137 | + request.setValue("application/json", forHTTPHeaderField: "Content-Type") 138 | + request.httpBody = try? JSONSerialization.data(withJSONObject: json) 139 | + 140 | + let task = URLSession.shared.dataTask(with: request) { data, response, error in 141 | + if let error = error { 142 | + // Handle network error 143 | + print("Error: \(error)") 144 | + return 145 | + } 146 | + 147 | + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { 148 | + // Handle server error 149 | + print("Error: Invalid response") 150 | + return 151 | + } 152 | + } 153 | + task.resume() 154 | + } catch { 155 | + return 156 | + } 157 | + } 158 | 159 | // Called on the main thread 160 | public func insertCannula(completion: @escaping (Result) -> Void) { 161 | @@ -834,6 +896,7 @@ extension OmnipodPumpManager { 162 | var podState = state.podState 163 | podState?.setupProgress = .completed 164 | state.updatePodStateFromPodComms(podState) 165 | + self.SiteChange() 166 | return .success(mockDelay) 167 | }) 168 | 169 | @@ -885,6 +948,7 @@ extension OmnipodPumpManager { 170 | ] 171 | 172 | let finishWait = try messageSender.insertCannula(optionalAlerts: alerts) 173 | + self.SiteChange() 174 | completion(.success(finishWait)) 175 | } catch let error { 176 | completion(.failure(.communication(error))) 177 | -------------------------------------------------------------------------------- /inline_functions/build_functions.sh: -------------------------------------------------------------------------------- 1 | #!inline common.sh 2 | 3 | ############################################################ 4 | # Common functions used by multiple build scripts 5 | # - Explanation of variables, Default values 6 | ############################################################ 7 | 8 | # Variables set by BuildXXX script that calls this inline script 9 | # 10 | # Required: BUILD_DIR 11 | # it is where the download folder will be created 12 | # For example: BUILD_DIR=~/Downloads/Build{app_name} 13 | # 14 | # Required: OVERRIDE_FILE 15 | # name of the automatic signing file 16 | # For example: OVERRIDE_FILE=LoopConfigOverride.xcconfig 17 | 18 | # Required: DEV_TEAM_SETTING_NAME 19 | # keyword used in the automatic signing file 20 | # e.g., ${DEV_TEAM_SETTING_NAME} = Apple Developer TeamID 21 | # Loop and Loop associated apps use: "LOOP_DEVELOPMENT_TEAM" 22 | # iAPS uses: "DEVELOPER_TEAM" 23 | 24 | # Default: some projects create or use the override file in the BUILD_DIR 25 | # Some, like iAPS, use a file in the downloaded clone itself 26 | # in that case, set USE_OVERRIDE_IN_REPO to 1 in the src/Build script 27 | : ${USE_OVERRIDE_IN_REPO:="0"} 28 | 29 | # Default: some projects use submodules (and need --recurse-submodule) 30 | # Some, like iAPS and LoopFollow, do not use submodules 31 | # in that case, set CLONE_SUB_MODULES to 0 in the src/Build script 32 | : ${CLONE_SUB_MODULES:="1"} 33 | 34 | # Accept build_warning before creating folders 35 | #!inline build_warning.sh 36 | 37 | # Messages prior to opening xcode 38 | #!inline before_final_return_message.sh 39 | 40 | # clean provisioning profiles saved on disk 41 | #!inline clean_profiles.sh 42 | 43 | ############################################################ 44 | # Common functions used by multiple build scripts 45 | # - Start of build_functions.sh common code 46 | ############################################################ 47 | 48 | SCRIPT_DIR="${BUILD_DIR}/Scripts" 49 | 50 | if [ ! -d "${BUILD_DIR}" ]; then 51 | mkdir "${BUILD_DIR}" 52 | fi 53 | 54 | ############################################################ 55 | # set up nominal values 56 | # these can be later overwritten by flags 57 | # for convenience when testing (or for advanced users) 58 | ############################################################ 59 | 60 | # FRESH_CLONE 61 | # Default value is 1, which means: 62 | # Download fresh clone every time script is run 63 | : ${FRESH_CLONE:="1"} 64 | # CLONE_STATUS used to test error messages 65 | # Default value is 0, which means no errors with clone 66 | : ${CLONE_STATUS:="0"} 67 | 68 | # Prepare date-time stamp for folder 69 | DOWNLOAD_DATE=$(date +'%y%m%d-%H%M') 70 | 71 | # This enables the selection of a custom branch via enviroment variable 72 | # It can also be passed in as argument $1 73 | # If passed in, it overwrites the environment variable 74 | # When CUSTOM_BRANCH is set, the menu which asks which branch is skipped 75 | CUSTOM_BRANCH=${1:-$CUSTOM_BRANCH} 76 | 77 | ############################################################ 78 | # Define the rest of the functions (usage defined above): 79 | ############################################################ 80 | 81 | #!inline building_verify_version.sh 82 | #!inline building_config_override.sh 83 | 84 | function standard_build_train() { 85 | verify_xcode_path 86 | check_versions 87 | clone_repo 88 | automated_clone_download_error_check 89 | check_config_override_existence_offer_to_configure 90 | ensure_a_year 91 | } 92 | 93 | function ensure_a_year() { 94 | section_separator 95 | 96 | echo -e "${INFO_FONT}Ensure a year by deleting old provisioning profiles${NC}" 97 | echo -e " Unless you have a specific reason, choose option 1\n" 98 | options=("Ensure a Year" "Skip" "$(exit_or_return_menu)") 99 | select opt in "${options[@]}" 100 | do 101 | case $opt in 102 | "Ensure a Year") 103 | clean_profiles 104 | break 105 | ;; 106 | "Skip") 107 | break 108 | ;; 109 | "$(exit_or_return_menu)") 110 | exit_script 111 | ;; 112 | *) # Invalid option 113 | invalid_entry 114 | ;; 115 | esac 116 | done 117 | } 118 | 119 | function clone_repo() { 120 | section_divider 121 | if [ "$SUPPRESS_BRANCH" == "true" ]; then 122 | LOCAL_DIR="${BUILD_DIR}/${APP_NAME}-${DOWNLOAD_DATE}" 123 | else 124 | LOCAL_DIR="${BUILD_DIR}/${APP_NAME}_${BRANCH//\//-}-${DOWNLOAD_DATE}" 125 | fi 126 | if [ ${FRESH_CLONE} == 1 ]; then 127 | mkdir "${LOCAL_DIR}" 128 | else 129 | LOCAL_DIR="${STARTING_DIR}" 130 | fi 131 | cd "${LOCAL_DIR}" 132 | if [ ${FRESH_CLONE} == 1 ]; then 133 | if [ "$SUPPRESS_BRANCH" == "true" ]; then 134 | echo -e " -- Downloading ${APP_NAME} to your Downloads folder --" 135 | else 136 | echo -e " -- Downloading ${APP_NAME} ${BRANCH} to your Downloads folder --" 137 | fi 138 | echo -e " ${LOCAL_DIR}\n" 139 | echo -e "Issuing this command:" 140 | if [[ $CLONE_SUB_MODULES == "1" ]]; then 141 | echo -e " git clone --branch=${BRANCH} --recurse-submodules ${REPO}" 142 | git clone --branch=$BRANCH --recurse-submodules $REPO 143 | else 144 | echo -e " git clone --branch=${BRANCH} ${REPO}" 145 | git clone --branch=$BRANCH $REPO 146 | fi 147 | clone_exit_status=$? 148 | else 149 | clone_exit_status=${CLONE_STATUS} 150 | fi 151 | } 152 | 153 | function automated_clone_download_error_check() { 154 | # Check if the clone was successful 155 | if [ $clone_exit_status -eq 0 ]; then 156 | # Use this flag to modify exit_or_return_menu 157 | echo -e "✅ ${SUCCESS_FONT}Successful Download. Proceed to the next step...${NC}" 158 | return_when_ready 159 | else 160 | echo -e "❌ ${ERROR_FONT}An error occurred during download. Please investigate the issue.${NC}" 161 | exit_message 162 | fi 163 | } 164 | 165 | function verify_xcode_path() { 166 | section_divider 167 | 168 | echo -e "Verifying xcode-select path...\n" 169 | 170 | # Get the path set by xcode-select 171 | xcode_path=$(xcode-select -p) 172 | 173 | # Check if the path contains "Xcode.app" 174 | if [[ -x "$xcode_path/usr/bin/xcodebuild" ]]; then 175 | echo -e "✅ ${SUCCESS_FONT}xcode-select path correctly set: $xcode_path${NC}" 176 | echo -e "Continuing the script..." 177 | else 178 | echo -e "❌ ${ERROR_FONT}xcode-select is not pointing to the correct Xcode path." 179 | echo -e " It is set to: $xcode_path${NC}" 180 | echo -e "Please choose an option below to proceed:\n" 181 | options=("Correct xcode-select path" "Skip" "$(exit_or_return_menu)") 182 | select opt in "${options[@]}" 183 | do 184 | case $opt in 185 | "Correct xcode-select path") 186 | xcode_path=$(mdfind -name Xcode.app 2>/dev/null) 187 | if [ -z "$xcode_path" ]; then 188 | echo -e "❌ ${ERROR_FONT}Xcode.app not found.${NC}" 189 | echo -e "Please use this guide to set the xcode-select path: https://loopkit.github.io/loopdocs/build/step9/#command-line-tools" 190 | exit_message 191 | else 192 | echo -e "Using this location: $xcode_path" 193 | DEVELOPER_DIR_PATH="$xcode_path/Contents/Developer" 194 | 195 | if [ ! -d "$DEVELOPER_DIR_PATH" ] 196 | then 197 | echo -e "❌ ${ERROR_FONT}Developer directory not found in Xcode.app. Please ensure you have the correct version of Xcode installed..${NC}" 198 | echo -e "Please use this guide to set the xcode-select path: https://loopkit.github.io/loopdocs/build/step9/#command-line-tools" 199 | exit_message 200 | else 201 | echo -e "You might be prompted for your password." 202 | echo -e " Use the password for logging into your Mac." 203 | sudo xcode-select -s "$DEVELOPER_DIR_PATH" 204 | 205 | # Check if the path was corrected successfully 206 | xcode_path=$(xcode-select -p) 207 | if [[ "$xcode_path" == *Xcode.app* ]]; then 208 | echo -e "✅ ${SUCCESS_FONT}xcode-select path has been corrected.${NC}" 209 | return_when_ready 210 | break 211 | else 212 | echo -e "❌ ${ERROR_FONT}Failed to set xcode-select path correctly.${NC}" 213 | exit_message 214 | fi 215 | fi 216 | fi 217 | ;; 218 | "Skip") 219 | break 220 | ;; 221 | "$(exit_or_return_menu)") 222 | exit_script 223 | ;; 224 | *) # Invalid option 225 | invalid_entry 226 | ;; 227 | esac 228 | done 229 | fi 230 | } 231 | 232 | function branch_select() { 233 | local url=$1 234 | local branch=$2 235 | local repo_name=$(basename $url .git) 236 | local app_name=${3:-$(basename $url .git)} 237 | local suppress_branch=${3:+true} 238 | 239 | REPO=$url 240 | BRANCH=$branch 241 | REPO_NAME=$repo_name 242 | APP_NAME=$app_name 243 | SUPPRESS_BRANCH=$suppress_branch 244 | } 245 | 246 | ############################################################ 247 | # End of functions used by script 248 | # - end of build_functions.sh common code 249 | ############################################################ 250 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Loop and Learn`: lnl-scripts 2 | 3 | ## Introduction 4 | 5 | These scripts simplify some tasks for building *Loop*, *Trio* and other DIY code from the GitHub repositories. 6 | 7 | The code that these scripts help you download, build or modify is provided as open source (OS), and it is your responsibility to review the code and understand how each app works. This code is experimental and intended for testing, research, and educational purposes; it is not approved for therapy. Patches or customizations are even more experimental. You take full responsibility for building and running an OS app, and you do so at your own risk. 8 | 9 | * [*Loop* Users](#loop-users) 10 | * [*Trio* Users](#trio-users) 11 | * [Individual Scripts](#individual-scripts) 12 | * [Build a Custom Branch](#build-a-custom-branch) 13 | * [Developer Tips](#developer-tips) 14 | * [Other Apps?](#other-apps) 15 | 16 | ### *Loop* Users 17 | 18 | Users of the *Loop* app should use the [Build Select Script](#loop-build-select-script). This script has a menu for users to choose the option they want. 19 | 20 | * Script returns to top-menu after each option completes 21 | * All Build scripts include automatic signing 22 | 23 | The **Build Select Script** provides these options: 24 | 25 | 1. Build *Loop* 26 | 2. Build Related Apps 27 | 3. Maintenance Utilities 28 | 4. Customization Select 29 | 30 | The `Build Loop` option offers your choice of the released (main) or development (dev) version of the *Loop* app. 31 | 32 | The `Build Related Apps` option includes *LoopFollow*, *LoopCaregiver* and *xDrip4iOS*. 33 | 34 | The `Maintenance Utilities` option includes `Delete Old Downloads` as well as other Xcode clean-up options. 35 | 36 | The `Customization Select` option runs the Customization Select script, which is used to customize the *Loop* app. 37 | 38 | In addition to selecting options from the top-menu of **BuildSelectScript**, each of the [individual scripts can be run directly](#individual-scripts). 39 | 40 | ### *Trio* Users 41 | 42 | Users of the *Trio* app should use the [*Trio* Build Select Script](#trio-build-select-script). This script has a menu for users to choose the option they want. 43 | 44 | * Script returns to top-menu after each option completes 45 | * All Build scripts include automatic signing 46 | 47 | The **Trio Build Select Script** provides these options: 48 | 49 | 1. Build *Trio* 50 | 2. Build Related Apps 51 | 3. Maintenance Utilities 52 | 53 | The `Build Trio` option offers your choice of the released (main) or development (dev) version of *Trio*. 54 | 55 | The `Build Related Apps` option includes *LoopFollow* and *xDrip4iOS*. 56 | 57 | The `Maintenance Utilities` option includes `Delete Old Downloads` as well as other Xcode clean-up options. 58 | 59 | In addition to selecting options from the top-menu of **TrioBuildSelectScript**, each of the [individual scripts can be run directly](#individual-scripts). 60 | 61 | ### `main` Branch 62 | 63 | Note: the commands in the README are for the main branch of the scripts. If you are a developer or tester using a different branch, please read [Developer Tips](#developer-tips). 64 | 65 | ## *Loop* Build Select Script 66 | 67 | This script is documented in 68 | 69 | * [LnL: Build Select Script](https://www.loopandlearn.org/build-select) 70 | * [*LoopDocs*: Build Select Script](https://loopkit.github.io/loopdocs/build/step14/#download-loop) 71 | 72 | ### Command to Execute Build Select Script 73 | 74 | 1. Open Terminal 75 | 2. Copy/Paste this code into Terminal (use copy icon, bottom right): 76 | 77 | ``` 78 | /bin/bash -c "$(curl -fsSL \ 79 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildSelectScript.sh)" 80 | ``` 81 | 82 | 3. Enter and follow prompts 83 | 84 | ## *Trio* Build Select Script 85 | 86 | This script is documented in 87 | 88 | * [*TrioDocs*](https://triodocs.org) 89 | 90 | The direct link (subject to change) is: 91 | 92 | * [*TrioDocs*: Build *Trio* with Script](https://triodocs.org/install/build/mac/build/) 93 | 94 | ### Command to Execute Build Select Script 95 | 96 | 1. Open Terminal 97 | 2. Copy/Paste this code into Terminal (use copy icon, bottom right): 98 | 99 | ``` 100 | /bin/bash -c "$(curl -fsSL \ 101 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/TrioBuildSelectScript.sh)" 102 | ``` 103 | 104 | 3. Enter and follow prompts 105 | 106 | ## Individual Scripts 107 | 108 | Individual scripts can be run directly with the commands listed below. 109 | 110 | 1. Open Terminal 111 | 2. Copy/Paste selected code into Terminal (use copy icon, bottom right): 112 | 3. Enter and follow prompts 113 | 114 | #### Scripts included in BuildSelectScript or TrioBuildSelectScript 115 | 116 | Use these commands to run a script directly instead of selecting it using the menu options in the Build Select Script. 117 | 118 | #### BuildLoop 119 | 120 | This builds the released or development version of *Loop*; you can choose the `main` or `dev` branch. 121 | 122 | ``` 123 | /bin/bash -c "$(curl -fsSL \ 124 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)" 125 | ``` 126 | 127 | #### BuildLoopFollow 128 | 129 | ``` 130 | /bin/bash -c "$(curl -fsSL \ 131 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoopFollow.sh)" 132 | ``` 133 | 134 | #### BuildLoopCaregiver 135 | 136 | ``` 137 | /bin/bash -c "$(curl -fsSL \ 138 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoopCaregiver.sh)" 139 | ``` 140 | 141 | #### BuildTrio 142 | 143 | ``` 144 | /bin/bash -c "$(curl -fsSL \ 145 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildTrio.sh)" 146 | ``` 147 | 148 | #### BuildxDrip4iOS 149 | 150 | ``` 151 | /bin/bash -c "$(curl -fsSL \ 152 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildxDrip4iOS.sh)" 153 | ``` 154 | 155 | #### CustomizationSelect 156 | 157 | Patches or customizations are even more experimental than the released version of *Loop*. It is your responsibility to understand the changes made to the *Loop* code when you apply, remove or update one of these customizations. Review the documentation: [CustomizationSelect](https://www.loopandlearn.org/build-select/#customization-select). 158 | 159 | 160 | ``` 161 | /bin/bash -c "$(curl -fsSL \ 162 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/CustomizationSelect.sh)" 163 | ``` 164 | 165 | #### DeleteOldDownloads 166 | 167 | ``` 168 | /bin/bash -c "$(curl -fsSL \ 169 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/DeleteOldDownloads.sh)" 170 | ``` 171 | 172 | ## Build a Custom Branch 173 | 174 | In addition to building a particular app with one or more options for the branch to be used, all the `Build` scripts will build a custom branch when called with a particular syntax. 175 | 176 | **Instructions for a custom branch:** 177 | 178 | * Add ` - branch_name` to the end of the command below 179 | * There must be a space after the ending quote followed by hyphen, another space and then the branch name 180 | * This bypasses the menu and downloads the desired branch from for the app 181 | 182 | The example below is for `BuildLoop` but the same method works for all the `Build` scripts. You cannot just copy and paste this command. You must edit it first. 183 | 184 | ``` 185 | /bin/bash -c "$(curl -fsSL \ 186 | https://raw.githubusercontent.com/loopandlearn/lnl-scripts/main/BuildLoop.sh)" - put_desired_branch_name_here 187 | ``` 188 | 189 | ## Developer Tips 190 | 191 | When these scripts are being modified and tested, the developers will be working in a feature or development (dev) branch. In addition, they can use some special flags to simplify and speed up testing. 192 | 193 | In order to test with a different branch, first configure that branch in Terminal with the export command. If using a branch other than dev, then modify the command with that branch name: 194 | 195 | ``` 196 | export SCRIPT_BRANCH=dev 197 | ``` 198 | 199 | Then for all commands found earlier in this README, replace the word main with $SCRIPT_BRANCH. 200 | 201 | When testing locally, there are other test variables you can configure. Be sure to read these two files: 202 | * `custom_config.sh` 203 | * `clear_custom_config.sh` 204 | 205 | ### Inlining Scripts 206 | 207 | This project uses a script inlining system to generate executable scripts from source files. The source files for each script are located in the src directory, and the generated scripts are output to the root directory. All inline scripts are in the inline_functions folder. 208 | 209 | To modify a script, simply edit the corresponding source file in the src directory, and then run the build script (`./build.sh`) to regenerate all the scripts. The build script will inline any required files and generate the final executable scripts. By adding an argument after `./build.sh`, you can limit the rebuild to the indicated script. 210 | 211 | To test a script during development, this command is helpful (example shown is for CustomatizationScript, but works for any script.) 212 | 213 | 1. Modify `src/CustomizationScript.sh` 214 | 1. Execute this command to rebuild and execute: 215 | * `./build.sh CustomizationScript.sh && ./CustomizationScript.sh` 216 | 217 | Note that the build system uses special comments to indicate which files should be inlined. Any line in a script that starts with #!inline will be replaced with the contents of the specified file. The build system will inline files up to a maximum depth of 10, to prevent infinite recursion. 218 | 219 | To learn more about the inlining process and how it works, please see the comments in the `build.sh` script. 220 | 221 | ### Environment Variable 222 | 223 | The available environment variables used by the scripts are set using the `export` command and cleared with the `unset` command. 224 | 225 | Once you use an export command, that environment variable stays set in that Terminal and will be used by the script. 226 | 227 | * You can use the unset command to stay in the same Terminal 228 | * You can use CMD-N while in any Terminal window to open a new Terminal window, then switch to the new window 229 | 230 | ## Other Apps? 231 | 232 | Several apps are no longer found in lnl-scripts. 233 | 234 | * The *GlucoseDirect* CGM app owner is no longer providing updates 235 | * *Trio* has removed *GlucoseDirect* as a CGM option 236 | * `Loop and Learn` provides customization support to add other CGM to the *Loop* app and *GlucoseDirect* is no longer provided as an option 237 | 238 | * *iAPS* 239 | * The script to build *iAPS* is still found in lnl-scripts but may be removed without notice 240 | * The *iAPS* app should only be used by folks who are involved in keeping up with the app and willing and able to build frequently without build-script support 241 | * The `Loop and Learn` Team suggest that *Trio* be used instead for most users 242 | -------------------------------------------------------------------------------- /patch/g6g7_sage.patch: -------------------------------------------------------------------------------- 1 | Submodule CGMBLEKit contains modified content 2 | diff --git a/CGMBLEKit/CGMBLEKit/TransmitterManager.swift b/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 3 | index 4942087..26d2181 100644 4 | --- a/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 5 | +++ b/CGMBLEKit/CGMBLEKit/TransmitterManager.swift 6 | @@ -9,7 +9,19 @@ import HealthKit 7 | import LoopKit 8 | import ShareClient 9 | import os.log 10 | - 11 | +import CommonCrypto 12 | + 13 | +extension String { 14 | + func sha1() -> String { 15 | + let data = Data(self.utf8) 16 | + var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) 17 | + data.withUnsafeBytes { 18 | + _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest) 19 | + } 20 | + let hexBytes = digest.map { String(format: "%02hhx", $0) } 21 | + return hexBytes.joined() 22 | + } 23 | +} 24 | 25 | public struct TransmitterManagerState: RawRepresentable, Equatable { 26 | public typealias RawValue = CGMManager.RawStateValue 27 | @@ -301,6 +313,74 @@ public class TransmitterManager: TransmitterDelegate { 28 | logDeviceCommunication("Error: \(error)", type: .error) 29 | } 30 | 31 | + //Patch for detecting a new G6 sensor 32 | + private func detectNewSensor() { 33 | + do { // Silence all errors and return 34 | + if let latestReading = latestReading { 35 | + let date = latestReading.sessionStartDate 36 | + let tenDaysInSeconds = 864000.0 37 | + let timeIntervalSinceNow = date.timeIntervalSinceNow 38 | + let now = Date() 39 | + 40 | + //Check that the sessionStartDate is reasonable 41 | + if timeIntervalSinceNow > -tenDaysInSeconds && date <= now { 42 | + 43 | + //Read the last date sent to nightscout 44 | + let interval = UserDefaults.standard.double(forKey: "lastSessionStartDate") 45 | + let lastsessionStartDate = Date(timeIntervalSinceReferenceDate: interval) 46 | + 47 | + //Check if the last value we sent to nightscout is not the same (with some margin for rounding errors) 48 | + if abs(date.timeIntervalSince(lastsessionStartDate)) > 60 { 49 | + let keychain = KeychainManager() 50 | + let credentials = try keychain.getInternetCredentials(account: "NightscoutAPI") 51 | + 52 | + let formatter = ISO8601DateFormatter() 53 | + formatter.timeZone = TimeZone(abbreviation: "UTC") 54 | + let dateString = formatter.string(from: date) 55 | + 56 | + let json: [String: Any] = [ 57 | + "enteredBy": "Loop", 58 | + "created_at": dateString, 59 | + "eventType": "Sensor Start", 60 | + "secret": credentials.password.sha1() 61 | + ] 62 | + 63 | + guard var urlComponents = URLComponents(url: credentials.url, resolvingAgainstBaseURL: false) else {return } 64 | + if urlComponents.path.hasSuffix("/") { 65 | + urlComponents.path = String(urlComponents.path.dropLast()) 66 | + } 67 | + urlComponents.path += "/api/v1/treatments.json" 68 | + 69 | + guard let apiURL = urlComponents.url else { return } 70 | + 71 | + var request = URLRequest(url: apiURL) 72 | + request.httpMethod = "POST" 73 | + request.setValue("application/json", forHTTPHeaderField: "Content-Type") 74 | + request.httpBody = try? JSONSerialization.data(withJSONObject: json) 75 | + 76 | + let task = URLSession.shared.dataTask(with: request) { data, response, error in 77 | + if let error = error { 78 | + // Handle network error 79 | + print("Error: \(error)") 80 | + return 81 | + } 82 | + 83 | + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { 84 | + // Handle server error 85 | + print("Error: Invalid response") 86 | + return 87 | + } 88 | + UserDefaults.standard.set(date.timeIntervalSinceReferenceDate, forKey: "lastSessionStartDate") 89 | + } 90 | + task.resume() 91 | + } 92 | + } 93 | + } 94 | + } catch { 95 | + return 96 | + } 97 | + } 98 | + 99 | public func transmitter(_ transmitter: Transmitter, didRead glucose: Glucose) { 100 | guard glucose != latestReading else { 101 | updateDelegate(with: .noData) 102 | @@ -309,6 +389,8 @@ public class TransmitterManager: TransmitterDelegate { 103 | 104 | latestReading = glucose 105 | 106 | + detectNewSensor() 107 | + 108 | logDeviceCommunication("New reading: \(glucose.readDate)", type: .receive) 109 | 110 | guard glucose.state.hasReliableGlucose else { 111 | Submodule G7SensorKit contains modified content 112 | diff --git a/G7SensorKit/G7SensorKit/G7CGMManager/G7Sensor.swift b/G7SensorKit/G7SensorKit/G7CGMManager/G7Sensor.swift 113 | index 3453929..b8cd99f 100644 114 | --- a/G7SensorKit/G7SensorKit/G7CGMManager/G7Sensor.swift 115 | +++ b/G7SensorKit/G7SensorKit/G7CGMManager/G7Sensor.swift 116 | @@ -10,7 +10,20 @@ import Foundation 117 | import CoreBluetooth 118 | import HealthKit 119 | import os.log 120 | - 121 | +import LoopKit 122 | +import CommonCrypto 123 | + 124 | +extension String { 125 | + func sha1() -> String { 126 | + let data = Data(self.utf8) 127 | + var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) 128 | + data.withUnsafeBytes { 129 | + _ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest) 130 | + } 131 | + let hexBytes = digest.map { String(format: "%02hhx", $0) } 132 | + return hexBytes.joined() 133 | + } 134 | +} 135 | 136 | public protocol G7SensorDelegate: AnyObject { 137 | func sensorDidConnect(_ sensor: G7Sensor, name: String) 138 | @@ -122,6 +135,73 @@ public final class G7Sensor: G7BluetoothManagerDelegate { 139 | return bluetoothManager.isConnected 140 | } 141 | 142 | + //Patch for detecting a new G6 sensor 143 | + private func detectNewSensor() { 144 | + do { // Silence all errors and return 145 | + if let date = self.activationDate { 146 | + let tenDaysInSeconds = 864000.0 147 | + let timeIntervalSinceNow = date.timeIntervalSinceNow 148 | + let now = Date() 149 | + 150 | + //Check that the sessionStartDate is reasonable 151 | + if timeIntervalSinceNow > -tenDaysInSeconds && date <= now { 152 | + 153 | + //Read the last date sent to nightscout 154 | + let interval = UserDefaults.standard.double(forKey: "lastG7SessionStartDate") 155 | + let lastsessionStartDate = Date(timeIntervalSinceReferenceDate: interval) 156 | + 157 | + //Check if the last value we sent to nightscout is not the same (with some margin for rounding errors) 158 | + if abs(date.timeIntervalSince(lastsessionStartDate)) > 60 { 159 | + let keychain = KeychainManager() 160 | + let credentials = try keychain.getInternetCredentials(account: "NightscoutAPI") 161 | + 162 | + let formatter = ISO8601DateFormatter() 163 | + formatter.timeZone = TimeZone(abbreviation: "UTC") 164 | + let dateString = formatter.string(from: date) 165 | + 166 | + let json: [String: Any] = [ 167 | + "enteredBy": "Loop", 168 | + "created_at": dateString, 169 | + "eventType": "Sensor Start", 170 | + "secret": credentials.password.sha1() 171 | + ] 172 | + 173 | + guard var urlComponents = URLComponents(url: credentials.url, resolvingAgainstBaseURL: false) else {return } 174 | + if urlComponents.path.hasSuffix("/") { 175 | + urlComponents.path = String(urlComponents.path.dropLast()) 176 | + } 177 | + urlComponents.path += "/api/v1/treatments.json" 178 | + 179 | + guard let apiURL = urlComponents.url else { return } 180 | + 181 | + var request = URLRequest(url: apiURL) 182 | + request.httpMethod = "POST" 183 | + request.setValue("application/json", forHTTPHeaderField: "Content-Type") 184 | + request.httpBody = try? JSONSerialization.data(withJSONObject: json) 185 | + 186 | + let task = URLSession.shared.dataTask(with: request) { data, response, error in 187 | + if let error = error { 188 | + // Handle network error 189 | + print("Error: \(error)") 190 | + return 191 | + } 192 | + 193 | + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { 194 | + // Handle server error 195 | + print("Error: Invalid response") 196 | + return 197 | + } 198 | + UserDefaults.standard.set(date.timeIntervalSinceReferenceDate, forKey: "lastG7SessionStartDate") 199 | + } 200 | + task.resume() 201 | + } 202 | + } 203 | + } 204 | + } catch { 205 | + return 206 | + } 207 | + } 208 | + 209 | private func handleGlucoseMessage(message: G7GlucoseMessage, peripheralManager: G7PeripheralManager) { 210 | activationDate = Date().addingTimeInterval(-TimeInterval(message.glucoseTimestamp)) 211 | peripheralManager.perform { (peripheral) in 212 | @@ -147,6 +227,8 @@ public final class G7Sensor: G7BluetoothManagerDelegate { 213 | self.activationDate = activationDate 214 | self.delegate?.sensor(self, didRead: message) 215 | self.bluetoothManager.stopScanning() 216 | + 217 | + self.detectNewSensor() 218 | } 219 | } 220 | } else if sensorID != nil { 221 | -------------------------------------------------------------------------------- /DeleteOldDownloads.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | 167 | # *** Start of inlined file: inline_functions/delete_old_downloads.sh *** 168 | # Flag to skip all deletions 169 | SKIP_ALL=false 170 | folder_count=0 171 | app_pattern_count=0 172 | 173 | # Default if environment variable is not set 174 | : ${DELETE_SELECTED_FOLDERS:="1"} 175 | 176 | function list_build_folders_when_testing() { 177 | # only echo pattern when testing 178 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 179 | echo 180 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 181 | echo -e " This is the list of all patterns that will be searched${NC}" 182 | echo 183 | for pattern in "${patterns[@]}"; do 184 | echo " $pattern" 185 | done 186 | section_divider 187 | fi 188 | } 189 | 190 | function delete_folders_except_latest() { 191 | local pattern="$1" 192 | local total_size=0 193 | local unsorted_folders=() 194 | 195 | # First loop for case-sensitive matching 196 | for entry in ~/Downloads/$pattern; do 197 | [ -d "$entry" ] && unsorted_folders+=("$entry") 198 | done 199 | 200 | # Second loop for case-insensitive matching, but only if "main" is in the pattern 201 | if [[ $pattern == *main* ]]; then 202 | for entry in ~/Downloads/${pattern//main/Main}; do 203 | [ -d "$entry" ] && unsorted_folders+=("$entry") 204 | done 205 | fi 206 | 207 | # Sort the folders array by date (newest first) 208 | IFS=$'\n' folders=($(sort -r <<<"${unsorted_folders[*]}")) 209 | IFS=$' \t\n' # Reset IFS to default value. 210 | 211 | if [ ${#folders[@]} -eq 0 ]; then 212 | return 213 | fi 214 | 215 | # increment because folders were found 216 | ((app_pattern_count=app_pattern_count+1)) 217 | 218 | if [ ${#folders[@]} -eq 1 ]; then 219 | echo "Only one download found for app pattern: '$pattern'" 220 | return 221 | fi 222 | 223 | section_divider 224 | 225 | echo "More than one download found for app pattern:" 226 | echo " '$pattern':" 227 | echo 228 | echo "Download Folder to Keep:" 229 | echo " ${folders[0]/#$HOME/~}" 230 | echo 231 | 232 | echo "Download Folder(s) that can be deleted:" 233 | 234 | for folder in "${folders[@]:1}"; do 235 | echo " ${folder/#$HOME/~}" 236 | total_size=$(($total_size + $(du -s "$folder" | awk '{print $1}'))) 237 | done 238 | 239 | echo 240 | echo -e " If Xcode is open in a folder you plan to delete," 241 | echo -e " ${INFO_FONT}Quit Xcode${NC} before deleting" 242 | 243 | total_size_mb=$(echo "scale=2; $total_size / 1024" | bc) 244 | echo 245 | echo "Total size to be deleted: $total_size_mb MB" 246 | section_divider 247 | 248 | options=( 249 | "Delete these Folders" 250 | "Skip delete at this location" 251 | "$(exit_or_return_menu)") 252 | actions=( 253 | "delete_selected_folders \"$pattern\"" 254 | "return" 255 | "exit_script") 256 | menu_select "${options[@]}" "${actions[@]}" 257 | } 258 | 259 | function delete_selected_folders() { 260 | local pattern="$1" 261 | local unsorted_folders=() 262 | 263 | # First loop for case-sensitive matching 264 | for entry in ~/Downloads/$pattern; do 265 | [ -d "$entry" ] && unsorted_folders+=("$entry") 266 | done 267 | 268 | # Second loop for case-insensitive matching, but only if "main" is in the pattern 269 | if [[ $pattern == *main* ]]; then 270 | for entry in ~/Downloads/${pattern//main/Main}; do 271 | [ -d "$entry" ] && unsorted_folders+=("$entry") 272 | done 273 | fi 274 | 275 | # Sort the folders array by date (newest first) 276 | IFS=$'\n' folders=($(sort -r <<<"${unsorted_folders[*]}")) 277 | IFS=$' \t\n' # Reset IFS to default value. 278 | echo 279 | 280 | this_pattern_count=0 281 | 282 | for folder in "${folders[@]:1}"; do 283 | if [ ${DELETE_SELECTED_FOLDERS} == 1 ]; then 284 | rm -rf "$folder" 285 | fi 286 | echo " Removed $folder" 287 | ((folder_count=folder_count+1)) 288 | ((this_pattern_count=this_pattern_count+1)) 289 | done 290 | 291 | echo -e "✅ ${SUCCESS_FONT}Deleted ${this_pattern_count} download folders for this app pattern${NC}" 292 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 293 | echo 294 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 295 | echo -e " So folders marked successfully deleted are still there${NC}" 296 | fi 297 | echo 298 | return_when_ready 299 | } 300 | 301 | function skip_all() { 302 | SKIP_ALL=true 303 | } 304 | 305 | function delete_old_downloads() { 306 | patterns=( 307 | "BuildLoop/Loop-*" 308 | "BuildLoop/Loop_lnl_patches-*" 309 | "BuildLoop/LoopCaregiver_dev-*" 310 | "BuildLoop/LoopWorkspace_*" 311 | "BuildLoop/Loop_dev-*" 312 | "BuildLoop/FreeAPS*" 313 | "BuildLoopFollow/LoopFollow_main*" 314 | "BuildLoopFollow/LoopFollow_Second_main*" 315 | "BuildLoopFollow/LoopFollow_Third_main*" 316 | "BuildLoopFollow/LoopFollow_dev*" 317 | "BuildxDrip4iOS/xDrip4iOS*" 318 | "Build_iAPS/iAPS_main_*" 319 | "Build_iAPS/iAPS_main-*" 320 | "Build_iAPS/iAPS_dev*" 321 | "BuildTrio/Trio_main*" 322 | "BuildTrio/Trio_dev*" 323 | ) 324 | 325 | list_build_folders_when_testing 326 | 327 | if [ "$SKIP_ALL" = false ] ; then 328 | section_divider 329 | echo "For each type of Build provided as a build script, " 330 | echo " you will be shown your most recent download" 331 | echo " and given the option to remove older downloads." 332 | 333 | for pattern in "${patterns[@]}"; do 334 | if [ "$SKIP_ALL" = false ] ; then 335 | delete_folders_except_latest "$pattern" 336 | else 337 | break 338 | fi 339 | done 340 | fi 341 | 342 | echo 343 | echo -e "✅ ${SUCCESS_FONT}Download folders have been examined for all app patterns.${NC}" 344 | echo -e " There were ${app_pattern_count} app patterns that contain one or more download" 345 | if [ ${folder_count} -eq 0 ]; then 346 | echo -e " No Download folders deleted" 347 | else 348 | echo -e " Deleted a total of ${folder_count} older download folders" 349 | fi 350 | if [ ${DELETE_SELECTED_FOLDERS} == 0 ]; then 351 | echo 352 | echo -e " ${INFO_FONT}Environment variable DELETE_SELECTED_FOLDERS is set to 0" 353 | echo -e " So folders marked successfully deleted are still there${NC}" 354 | fi 355 | } 356 | # *** End of inlined file: inline_functions/delete_old_downloads.sh *** 357 | 358 | 359 | delete_old_downloads 360 | 361 | # *** End of inlined file: src/DeleteOldDownloads.sh *** 362 | 363 | -------------------------------------------------------------------------------- /TrioBuildSelectScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script TrioBuildSelectScript.sh 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | # *** Start of inlined file: inline_functions/build_warning.sh *** 167 | ############################################################ 168 | # warning used by all scripts that build an app 169 | ############################################################ 170 | 171 | function open_source_warning() { 172 | # Skip open source warning if opted out using env variable or this script is run from another script 173 | if [ "${SKIP_OPEN_SOURCE_WARNING}" = "1" ] || [ "$0" = "_" ]; then return; fi 174 | 175 | local documentation_link="${1:-}" 176 | 177 | section_separator 178 | 179 | echo -e "${INFO_FONT}*** IMPORTANT ***${NC}\n" 180 | echo -e "This project is:" 181 | echo -e "${INFO_FONT} Open Source software" 182 | echo -e " Not \"approved\" for therapy${NC}" 183 | echo -e "" 184 | echo -e " You take full responsibility when you build" 185 | echo -e " or run an open source app, and" 186 | echo -e " ${INFO_FONT}you do so at your own risk.${NC}" 187 | echo -e "" 188 | echo -e "To increase (decrease) font size" 189 | echo -e " Hold down the CMD key and hit + (-)" 190 | echo -e "\n${INFO_FONT}By typing 1 and ENTER, you indicate you understand" 191 | echo -e "\n--------------------------------\n${NC}" 192 | 193 | options=("Agree" "Cancel") 194 | select opt in "${options[@]}"; do 195 | case $opt in 196 | "Agree") 197 | break 198 | ;; 199 | "Cancel") 200 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 201 | exit_script 202 | ;; 203 | *) 204 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 205 | invalid_entry 206 | exit_script 207 | ;; 208 | esac 209 | done 210 | 211 | # Warning has been issued 212 | SKIP_OPEN_SOURCE_WARNING=1 213 | 214 | echo -e "${NC}\n\n\n\n" 215 | } 216 | # *** End of inlined file: inline_functions/build_warning.sh *** 217 | 218 | 219 | # *** Start of inlined file: inline_functions/run_script.sh *** 220 | # The function fetches and executes a script either from LnL GitHub repository 221 | # or from the current local directory (if LOCAL_SCRIPT is set to "1"). 222 | # The script is executed with "_" as parameter $0, telling the script that it is 223 | # run from within the ecosystem of LnL. 224 | # run_script accepts two parameters: 225 | # 1. script_name: The name of the script to be executed. 226 | # 2. extra_arg (optional): An additional argument to be passed to the script. 227 | # If the script fails to execute, the function prints an error message and terminates 228 | # the entire shell script with a non-zero status code. 229 | run_script() { 230 | local script_name=$1 231 | local extra_arg=$2 232 | echo -e "\n--------------------------------\n" 233 | echo -e "Executing Script: $script_name" 234 | echo -e "\n--------------------------------\n" 235 | 236 | if [[ ${LOCAL_SCRIPT:-0} -eq 0 ]]; then 237 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/$SCRIPT_BRANCH/$script_name)" _ "$extra_arg" 238 | else 239 | /bin/bash -c "$(cat $script_name)" _ "$extra_arg" 240 | fi 241 | 242 | if [ $? -ne 0 ]; then 243 | echo "Error: Failed to execute $script_name" 244 | exit 1 245 | fi 246 | } 247 | # *** End of inlined file: inline_functions/run_script.sh *** 248 | 249 | 250 | # *** Start of inlined file: inline_functions/utility_scripts.sh *** 251 | function utility_scripts { 252 | section_separator 253 | echo -e "${INFO_FONT}These utility scripts automate several cleanup actions${NC}" 254 | echo -e "" 255 | echo -e " 1. Delete Old Downloads:" 256 | echo -e " This will keep the most recent download for each build type" 257 | echo -e " It asks before deleting any folders" 258 | echo -e " 2. Clean Derived Data:" 259 | echo -e " Free space on your disk from old Xcode builds." 260 | echo -e " You should quit Xcode before running this script." 261 | echo -e " 3. Xcode Cleanup (The Big One):" 262 | echo -e " Clears more disk space filled up by using Xcode." 263 | echo -e " * Use after uninstalling Xcode prior to new installation" 264 | echo -e " * It can free up a substantial amount of disk space" 265 | echo -e " You should quit Xcode before running this script." 266 | echo -e " 4. Clean Profiles:" 267 | echo -e " Deletes any provisioning profiles on your Mac" 268 | echo -e " * Xcode will generate new ones" 269 | echo -e " * Ensures the next app you build with Xcode will last a year" 270 | section_divider 271 | echo -e "${INFO_FONT}Pay attention - quit Xcode before selecting some options${NC}" 272 | section_divider 273 | 274 | options=( 275 | "Delete Old Downloads" 276 | "Clean Derived Data (Quit Xcode)" 277 | "Xcode Cleanup (Quit Xcode)" 278 | "Clean Profiles" 279 | "Return to Menu" 280 | ) 281 | actions=( 282 | "run_script 'DeleteOldDownloads.sh'" 283 | "run_script 'CleanDerived.sh'" 284 | "run_script 'XcodeClean.sh'" 285 | "run_script 'CleanProfiles.sh'" 286 | return 287 | ) 288 | menu_select "${options[@]}" "${actions[@]}" 289 | return_when_ready 290 | } 291 | # *** End of inlined file: inline_functions/utility_scripts.sh *** 292 | 293 | 294 | # Set default values only if they haven't been defined as environment variables 295 | : ${SCRIPT_BRANCH:="main"} 296 | 297 | function placeholder() { 298 | section_divider 299 | echo -e " The feature is not available, coming soon" 300 | echo -e " This is a placeholder" 301 | return 302 | } 303 | 304 | ############################################################ 305 | # The rest of this is specific to the particular script 306 | ############################################################ 307 | 308 | # use app_name instead of hard-coded strings 309 | app_name="Trio" 310 | 311 | FIRST_TIME="1" 312 | SKIP_OPEN_SOURCE_WARNING="0" 313 | 314 | function first_time_menu() { 315 | section_separator 316 | echo -e "${INFO_FONT}Welcome to the Loop and Learn\n ${app_name} Build-Select Script\n${NC}" 317 | echo "Choose from one of these options:" 318 | echo " 1 Download and Build ${app_name}" 319 | echo " 2 Download and Build Related Apps" 320 | echo " 3 Run Maintenance Utilities" 321 | echo " 4 Exit Script" 322 | echo "" 323 | echo "After completing a given option, you can choose another or exit the script" 324 | FIRST_TIME="0" 325 | } 326 | 327 | ############################################################ 328 | # Welcome & What to do selection 329 | ############################################################ 330 | 331 | while true; do 332 | if [ "${FIRST_TIME}" = "1" ]; then 333 | first_time_menu 334 | fi 335 | section_divider 336 | 337 | options=(\ 338 | "Build ${app_name}" \ 339 | "Build Related Apps" \ 340 | "Maintenance Utilities" \ 341 | "Exit Script") 342 | actions=(\ 343 | "WHICH=${app_name}" \ 344 | "WHICH=OtherApps" \ 345 | "WHICH=UtilityScripts" \ 346 | "exit_script") 347 | menu_select "${options[@]}" "${actions[@]}" 348 | 349 | if [ "$WHICH" = "${app_name}" ]; then 350 | 351 | # Issue Warning if not done previously 352 | open_source_warning 353 | 354 | run_script "Build${app_name}.sh" $CUSTOM_BRANCH 355 | 356 | 357 | elif [ "$WHICH" = "OtherApps" ]; then 358 | 359 | # Issue Warning if not done previously 360 | open_source_warning 361 | 362 | section_separator 363 | echo -e "Select the app you want to build" 364 | echo -e " Each selection will indicate documentation links" 365 | echo -e " Please read the documentation before using the app" 366 | echo -e "" 367 | options=(\ 368 | "Build Loop Follow" \ 369 | "Build xDrip4iOS" \ 370 | "Return to Menu") 371 | actions=(\ 372 | "WHICH=LoopFollow" \ 373 | "WHICH=xDrip4iOS" \ 374 | return) 375 | menu_select "${options[@]}" "${actions[@]}" 376 | if [ "$WHICH" = "LoopFollow" ]; then 377 | run_script "BuildLoopFollow.sh" $CUSTOM_BRANCH 378 | elif [ "$WHICH" = "xDrip4iOS" ]; then 379 | run_script "BuildxDrip4iOS.sh" $CUSTOM_BRANCH 380 | fi 381 | 382 | elif [ "$WHICH" = "UtilityScripts" ]; then 383 | utility_scripts 384 | fi 385 | done 386 | # *** End of inlined file: src/TrioBuildSelectScript.sh *** 387 | 388 | -------------------------------------------------------------------------------- /BuildSelectScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash # script BuildSelectScript.sh 2 | # ----------------------------------------------------------------------------- 3 | # This file is GENERATED. DO NOT EDIT directly. 4 | # If you want to modify this file, edit the corresponding file in the src/ 5 | # directory and then run the build script to regenerate this output file. 6 | # ----------------------------------------------------------------------------- 7 | 8 | 9 | # *** Start of inlined file: inline_functions/common.sh *** 10 | STARTING_DIR="${PWD}" 11 | 12 | ############################################################ 13 | # define some font styles and colors 14 | ############################################################ 15 | 16 | # remove special font 17 | NC='\033[0m' 18 | # add special font 19 | #INFO_FONT='\033[1;36m' 20 | INFO_FONT='\033[1m' 21 | SUCCESS_FONT='\033[1;32m' 22 | ERROR_FONT='\033[1;31m' 23 | 24 | function section_divider() { 25 | echo -e "" 26 | echo -e "--------------------------------" 27 | echo -e "" 28 | } 29 | 30 | function section_separator() { 31 | # Clears the screen without clearing the scrollback buffer, suppressing any error messages 32 | echo -e "\033[2J\033[H" 2>/dev/null 33 | section_divider 34 | } 35 | 36 | function return_when_ready() { 37 | echo -e "${INFO_FONT}Return when ready to continue${NC}" 38 | read -p "" dummy 39 | } 40 | 41 | # Skip if this script is called from another script, then this has already been displayed 42 | if [ "$0" != "_" ]; then 43 | # Inform the user about env variables set 44 | # Variables definition 45 | variables=( 46 | "SCRIPT_BRANCH: Indicates the lnl-scripts branch in use." 47 | "LOCAL_SCRIPT: Set to 1 to run scripts from the local directory." 48 | "FRESH_CLONE: Lets you use an existing clone (saves time)." 49 | "CLONE_STATUS: Can be set to 0 for success (default) or 1 for error." 50 | "SKIP_OPEN_SOURCE_WARNING: If set, skips the open source warning for build scripts." 51 | "CUSTOM_URL: Overrides the repo url." 52 | "CUSTOM_BRANCH: Overrides the branch used for git clone." 53 | "CUSTOM_MACOS_VER: Overrides the detected macOS version." 54 | "CUSTOM_XCODE_VER: Overrides the detected Xcode version." 55 | "DELETE_SELECTED_FOLDERS: Echoes folder names but does not delete them" 56 | "PATCH_BRANCH: Indicates the source branch for patches." 57 | "PATCH_REPO: Specifies the URL of the patch source repository." 58 | "LOCAL_PATCH_FOLDER: Defines a local directory for sourcing patches." 59 | "CUSTOMIZATION_DEBUG: Determines the verbosity of the customization debug output." 60 | ) 61 | 62 | # Flag to check if any variable is set 63 | any_variable_set=false 64 | 65 | # Iterate over each variable 66 | for var in "${variables[@]}"; do 67 | # Split the variable name and description 68 | IFS=":" read -r name description <<<"$var" 69 | 70 | # Check if the variable is set 71 | if [ -n "${!name}" ]; then 72 | # If this is the first variable set, print the initial message 73 | if ! $any_variable_set; then 74 | section_separator 75 | echo -e "For your information, you are running this script in customized mode" 76 | echo -e "You might be using a branch other than main, and using SCRIPT_BRANCH" 77 | echo -e "Developers might have additional environment variables set:" 78 | any_variable_set=true 79 | fi 80 | 81 | # Print the variable name, value, and description 82 | echo " - $name: ${!name}" 83 | echo " $description" 84 | fi 85 | done 86 | if $any_variable_set; then 87 | echo 88 | echo "To clear the values, close this terminal and start a new one." 89 | echo "Sleeping for 2 sec then continuing" 90 | sleep 2 91 | fi 92 | fi 93 | 94 | function choose_option() { 95 | echo -e "Type a number from the list below and return to proceed." 96 | section_divider 97 | } 98 | 99 | function invalid_entry() { 100 | echo -e "\n${ERROR_FONT}Invalid option${NC}\n" 101 | } 102 | 103 | function do_continue() { 104 | : 105 | } 106 | 107 | function menu_select() { 108 | choose_option 109 | 110 | local options=("${@:1:$#/2}") 111 | local actions=("${@:$(($# + 1))/2+1}") 112 | 113 | while true; do 114 | select opt in "${options[@]}"; do 115 | for i in $(seq 0 $((${#options[@]} - 1))); do 116 | if [ "$opt" = "${options[$i]}" ]; then 117 | eval "${actions[$i]}" 118 | return 119 | fi 120 | done 121 | invalid_entry 122 | break 123 | done 124 | done 125 | } 126 | 127 | function exit_or_return_menu() { 128 | if [ "$0" != "_" ]; then 129 | # Called directly 130 | echo "Exit Script" 131 | else 132 | # Called from BuildSelectScript 133 | echo "Return to Menu" 134 | fi 135 | } 136 | 137 | function exit_script() { 138 | if [ "$0" != "_" ]; then 139 | # Called directly 140 | exit_message 141 | else 142 | # Called from BuildSelectScript 143 | exit 0 144 | fi 145 | } 146 | 147 | function exit_message() { 148 | section_divider 149 | echo -e "${INFO_FONT}Exit from Script${NC}\n" 150 | echo -e " You may close the terminal" 151 | echo -e "or" 152 | echo -e " You can press the up arrow ⬆️ on the keyboard" 153 | echo -e " and return to repeat script from beginning" 154 | section_divider 155 | exit 0 156 | } 157 | 158 | function erase_previous_line { 159 | if [ -n "$TERM" ]; then 160 | (tput cuu1 && tput el) 2>/dev/null || true 161 | fi 162 | } 163 | # *** End of inlined file: inline_functions/common.sh *** 164 | 165 | 166 | # *** Start of inlined file: inline_functions/build_warning.sh *** 167 | ############################################################ 168 | # warning used by all scripts that build an app 169 | ############################################################ 170 | 171 | function open_source_warning() { 172 | # Skip open source warning if opted out using env variable or this script is run from another script 173 | if [ "${SKIP_OPEN_SOURCE_WARNING}" = "1" ] || [ "$0" = "_" ]; then return; fi 174 | 175 | local documentation_link="${1:-}" 176 | 177 | section_separator 178 | 179 | echo -e "${INFO_FONT}*** IMPORTANT ***${NC}\n" 180 | echo -e "This project is:" 181 | echo -e "${INFO_FONT} Open Source software" 182 | echo -e " Not \"approved\" for therapy${NC}" 183 | echo -e "" 184 | echo -e " You take full responsibility when you build" 185 | echo -e " or run an open source app, and" 186 | echo -e " ${INFO_FONT}you do so at your own risk.${NC}" 187 | echo -e "" 188 | echo -e "To increase (decrease) font size" 189 | echo -e " Hold down the CMD key and hit + (-)" 190 | echo -e "\n${INFO_FONT}By typing 1 and ENTER, you indicate you understand" 191 | echo -e "\n--------------------------------\n${NC}" 192 | 193 | options=("Agree" "Cancel") 194 | select opt in "${options[@]}"; do 195 | case $opt in 196 | "Agree") 197 | break 198 | ;; 199 | "Cancel") 200 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 201 | exit_script 202 | ;; 203 | *) 204 | echo -e "\n${INFO_FONT}User did not agree to terms of use.${NC}\n\n" 205 | invalid_entry 206 | exit_script 207 | ;; 208 | esac 209 | done 210 | 211 | # Warning has been issued 212 | SKIP_OPEN_SOURCE_WARNING=1 213 | 214 | echo -e "${NC}\n\n\n\n" 215 | } 216 | # *** End of inlined file: inline_functions/build_warning.sh *** 217 | 218 | 219 | # *** Start of inlined file: inline_functions/run_script.sh *** 220 | # The function fetches and executes a script either from LnL GitHub repository 221 | # or from the current local directory (if LOCAL_SCRIPT is set to "1"). 222 | # The script is executed with "_" as parameter $0, telling the script that it is 223 | # run from within the ecosystem of LnL. 224 | # run_script accepts two parameters: 225 | # 1. script_name: The name of the script to be executed. 226 | # 2. extra_arg (optional): An additional argument to be passed to the script. 227 | # If the script fails to execute, the function prints an error message and terminates 228 | # the entire shell script with a non-zero status code. 229 | run_script() { 230 | local script_name=$1 231 | local extra_arg=$2 232 | echo -e "\n--------------------------------\n" 233 | echo -e "Executing Script: $script_name" 234 | echo -e "\n--------------------------------\n" 235 | 236 | if [[ ${LOCAL_SCRIPT:-0} -eq 0 ]]; then 237 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/loopandlearn/lnl-scripts/$SCRIPT_BRANCH/$script_name)" _ "$extra_arg" 238 | else 239 | /bin/bash -c "$(cat $script_name)" _ "$extra_arg" 240 | fi 241 | 242 | if [ $? -ne 0 ]; then 243 | echo "Error: Failed to execute $script_name" 244 | exit 1 245 | fi 246 | } 247 | # *** End of inlined file: inline_functions/run_script.sh *** 248 | 249 | 250 | # *** Start of inlined file: inline_functions/utility_scripts.sh *** 251 | function utility_scripts { 252 | section_separator 253 | echo -e "${INFO_FONT}These utility scripts automate several cleanup actions${NC}" 254 | echo -e "" 255 | echo -e " 1. Delete Old Downloads:" 256 | echo -e " This will keep the most recent download for each build type" 257 | echo -e " It asks before deleting any folders" 258 | echo -e " 2. Clean Derived Data:" 259 | echo -e " Free space on your disk from old Xcode builds." 260 | echo -e " You should quit Xcode before running this script." 261 | echo -e " 3. Xcode Cleanup (The Big One):" 262 | echo -e " Clears more disk space filled up by using Xcode." 263 | echo -e " * Use after uninstalling Xcode prior to new installation" 264 | echo -e " * It can free up a substantial amount of disk space" 265 | echo -e " You should quit Xcode before running this script." 266 | echo -e " 4. Clean Profiles:" 267 | echo -e " Deletes any provisioning profiles on your Mac" 268 | echo -e " * Xcode will generate new ones" 269 | echo -e " * Ensures the next app you build with Xcode will last a year" 270 | section_divider 271 | echo -e "${INFO_FONT}Pay attention - quit Xcode before selecting some options${NC}" 272 | section_divider 273 | 274 | options=( 275 | "Delete Old Downloads" 276 | "Clean Derived Data (Quit Xcode)" 277 | "Xcode Cleanup (Quit Xcode)" 278 | "Clean Profiles" 279 | "Return to Menu" 280 | ) 281 | actions=( 282 | "run_script 'DeleteOldDownloads.sh'" 283 | "run_script 'CleanDerived.sh'" 284 | "run_script 'XcodeClean.sh'" 285 | "run_script 'CleanProfiles.sh'" 286 | return 287 | ) 288 | menu_select "${options[@]}" "${actions[@]}" 289 | return_when_ready 290 | } 291 | # *** End of inlined file: inline_functions/utility_scripts.sh *** 292 | 293 | 294 | # Set default values only if they haven't been defined as environment variables 295 | : ${SCRIPT_BRANCH:="main"} 296 | 297 | function placeholder() { 298 | section_divider 299 | echo -e " The feature is not available, coming soon" 300 | echo -e " This is a placeholder" 301 | return 302 | } 303 | 304 | ############################################################ 305 | # The rest of this is specific to the particular script 306 | ############################################################ 307 | 308 | FIRST_TIME="1" 309 | SKIP_OPEN_SOURCE_WARNING="0" 310 | 311 | function first_time_menu() { 312 | section_separator 313 | echo -e "${INFO_FONT}Welcome to the Loop and Learn\n Build-Select Script\n${NC}" 314 | echo "Choose from one of these options:" 315 | echo " 1 Download and Build Loop" 316 | echo " 2 Download and Build Related Apps" 317 | echo " 3 Run Maintenance Utilities" 318 | echo " 4 Run Customization Select" 319 | echo " 5 Exit Script" 320 | echo "" 321 | echo "After completing a given option, you can choose another or exit the script" 322 | FIRST_TIME="0" 323 | } 324 | 325 | ############################################################ 326 | # Welcome & What to do selection 327 | ############################################################ 328 | 329 | while true; do 330 | if [ "${FIRST_TIME}" = "1" ]; then 331 | first_time_menu 332 | fi 333 | section_divider 334 | 335 | options=(\ 336 | "Build Loop" \ 337 | "Build Related Apps" \ 338 | "Maintenance Utilities" \ 339 | "Customization Select" \ 340 | "Exit Script") 341 | actions=(\ 342 | "WHICH=Loop" \ 343 | "WHICH=OtherApps" \ 344 | "WHICH=UtilityScripts" \ 345 | "WHICH=CustomizationScripts" \ 346 | "exit_script") 347 | menu_select "${options[@]}" "${actions[@]}" 348 | 349 | if [ "$WHICH" = "Loop" ]; then 350 | 351 | # Issue Warning if not done previously 352 | open_source_warning 353 | 354 | run_script "BuildLoop.sh" $CUSTOM_BRANCH 355 | 356 | 357 | elif [ "$WHICH" = "OtherApps" ]; then 358 | 359 | # Issue Warning if not done previously 360 | open_source_warning 361 | 362 | section_separator 363 | echo -e "Select the app you want to build" 364 | echo -e " Each selection will indicate documentation links" 365 | echo -e " Please read the documentation before using the app" 366 | echo -e "" 367 | options=(\ 368 | "Build Loop Follow" \ 369 | "Build LoopCaregiver" \ 370 | "Build xDrip4iOS" \ 371 | "Return to Menu") 372 | actions=(\ 373 | "WHICH=LoopFollow" \ 374 | "WHICH=LoopCaregiver" \ 375 | "WHICH=xDrip4iOS" \ 376 | return) 377 | menu_select "${options[@]}" "${actions[@]}" 378 | if [ "$WHICH" = "LoopFollow" ]; then 379 | run_script "BuildLoopFollow.sh" $CUSTOM_BRANCH 380 | elif [ "$WHICH" = "LoopCaregiver" ]; then 381 | run_script "BuildLoopCaregiver.sh" $CUSTOM_BRANCH 382 | elif [ "$WHICH" = "xDrip4iOS" ]; then 383 | run_script "BuildxDrip4iOS.sh" $CUSTOM_BRANCH 384 | fi 385 | 386 | elif [ "$WHICH" = "UtilityScripts" ]; then 387 | utility_scripts 388 | else 389 | run_script "CustomizationSelect.sh" $CUSTOM_BRANCH 390 | fi 391 | done 392 | # *** End of inlined file: src/BuildSelectScript.sh *** 393 | 394 | -------------------------------------------------------------------------------- /inline_functions/patch_functions.sh: -------------------------------------------------------------------------------- 1 | #!inline common.sh 2 | 3 | # Check if both app_name and app_folder_name are set 4 | if [ -z "$app_name" ] || [ -z "$app_folder_name" ]; then 5 | echo "Error: Both app_name and app_folder_name need to be set." 6 | exit 1 7 | fi 8 | 9 | # set to 1 for debug (verbose output) mode at beginning of script 10 | # set to 2 for debug (verbose output) mode for every refresh 11 | : ${CUSTOMIZATION_DEBUG:="0"} 12 | 13 | : ${PATCH_BRANCH:="main"} 14 | : ${PATCH_REPO:="https://github.com/loopandlearn/customization.git"} 15 | 16 | REPO_NAME=$(basename "${PATCH_REPO}" .git) 17 | 18 | # set fixed numbers for certain actions 19 | SLEEP_TIME_AFTER_SUCCESS=1 20 | REMOVE_CUSTOMIZATION_MENU_ITEM=40 21 | UPDATE_CUSTOMIZATION_MENU_ITEM=45 22 | EXIT_MENU_ITEM=50 23 | EXIT_OPEN_XCODE_MENU_ITEM=60 24 | MAX_MENU_ITEM=$EXIT_OPEN_XCODE_MENU_ITEM 25 | 26 | one_time_flag=0 27 | 28 | # Determines if the $0 parameter should be used as a customization 29 | function param_zero_is_customization { 30 | if [[ $0 == *CustomizationSelect.sh ]] || [[ $0 == "_" ]] || [[ $0 == "/bin/bash" ]] || [[ -z $0 ]]; then 31 | return 1 # This means false in bash 32 | else 33 | return 0 # This means true in bash 34 | fi 35 | } 36 | 37 | function message_about_display() { 38 | echo -e "${INFO_FONT}You may need to scroll up to read everything${NC}" 39 | echo -e "${INFO_FONT} or drag corner to make terminal taller${NC}" 40 | echo -e "${SUCCESS_FONT}There is $SLEEP_TIME_AFTER_SUCCESS second pause for a success message${NC}" 41 | echo " Do not worry if it goes by too quickly to read" 42 | echo -e "${INFO_FONT}Errors will be reported and script will wait for user${NC}" 43 | echo 44 | one_time_flag=1 45 | } 46 | 47 | warning_flag=0 48 | 49 | function warning_message() { 50 | echo -e "${INFO_FONT} *** WARNING ***${NC}" 51 | echo -e "${INFO_FONT}Customizations are even more experimental than the released version${NC}" 52 | echo -e "${INFO_FONT}of ${app_name}. It is your responsibility to understand the changes${NC}" 53 | echo -e "${INFO_FONT}expected when you apply, remove or update one of these customizations${NC}" 54 | echo 55 | warning_flag=1 56 | } 57 | 58 | customization=() 59 | folder=() 60 | message_function=() 61 | status=() 62 | patch=() 63 | clean_build=() 64 | final_message=() 65 | 66 | function add_customization() { 67 | customization+=("$1") 68 | folder+=("$2") 69 | message_function+=("$3") 70 | clean_build+=("$4") 71 | final_message+=("$5") 72 | } 73 | 74 | folder_translation_from=() 75 | folder_translation_to=() 76 | 77 | function add_translation() { 78 | folder_translation_from+=("$1") 79 | folder_translation_to+=("$2") 80 | } 81 | 82 | function translate_folder() { 83 | local folder_name="$1" 84 | for i in "${!folder_translation_from[@]}"; do 85 | if [[ "$folder_name" == "${folder_translation_from[$i]}" ]]; then 86 | echo "${folder_translation_to[$i]}" 87 | return 88 | fi 89 | done 90 | echo "$folder_name" 91 | } 92 | 93 | function refresh_status() { 94 | # Status documentation 95 | # 0 Patch not applied (a current version of the patch is possible to apply) 96 | # 1 Patch applied (a current version of the patch can be reversed) 97 | # 2 Old version applied (an archived version of the patch can be reversed, possible upgrade) 98 | # 3 Patch not applicable due to conflicts (no version of the patch can be applied or reversed) 99 | 100 | # Iterate through each customization 101 | for ((index=0; index<${#customization[@]}; index++)); do 102 | if [ -z "$LOCAL_PATCH_FOLDER" ]; then 103 | local patch_folder="$mytmpdir/${REPO_NAME}/${folder[$index]}" 104 | else 105 | local patch_folder="$LOCAL_PATCH_FOLDER/${folder[$index]}" 106 | fi 107 | local archive_folder="$patch_folder/archive" 108 | 109 | # Initialize status as not applicable 110 | status[$index]=3 111 | patch[$index]="" 112 | 113 | # Check all patches in the current patch folder 114 | for patch_file in $(ls "$patch_folder"/*.patch | sort); do 115 | if [ -f "$patch_file" ]; then 116 | # Try to apply the patch 117 | if git apply --check "$patch_file" >/dev/null 2>&1; then 118 | status[$index]=0 119 | patch[$index]="$patch_file" 120 | break 121 | fi 122 | 123 | # Try to reverse the patch 124 | if git apply --reverse --check "$patch_file" >/dev/null 2>&1; then 125 | status[$index]=1 126 | patch[$index]="$patch_file" 127 | break 128 | fi 129 | fi 130 | done 131 | 132 | # If no current patch can be applied or reversed, check archived patches 133 | # If the current patch is not applied, there might be a reversable archived patch = upgrade 134 | if [ ${status[$index]} -eq 3 ] || [ ${status[$index]} -eq 0 ]; then 135 | for patch_file in "$archive_folder"/*.patch; do 136 | if [ -f "$patch_file" ]; then 137 | # Try to reverse the patch 138 | if git apply --reverse --check "$patch_file" >/dev/null 2>&1; then 139 | status[$index]=2 140 | patch[$index]="$patch_file" 141 | break 142 | fi 143 | fi 144 | done 145 | fi 146 | done 147 | if [ $CUSTOMIZATION_DEBUG -eq 2 ]; then 148 | debug_printout 149 | fi 150 | } 151 | 152 | function debug_printout() { 153 | echo "Customizations:" 154 | for ((index=0; index<${#customization[@]}; index++)); do 155 | echo "$index: ${customization[$index]}" 156 | done 157 | 158 | echo "Folders:" 159 | for ((index=0; index<${#folder[@]}; index++)); do 160 | echo "$index: ${folder[$index]}" 161 | done 162 | 163 | echo "Statuses:" 164 | for ((index=0; index<${#status[@]}; index++)); do 165 | echo "$index: ${status[$index]}" 166 | done 167 | 168 | echo "Patches:" 169 | for ((index=0; index<${#patch[@]}; index++)); do 170 | echo "$index: ${patch[$index]}" 171 | done 172 | } 173 | 174 | # Deletes the temp directory 175 | function cleanup { 176 | if [ "$GITHUB_ACTIONS" != "true" ]; then 177 | echo "Deleting temp working directory $mytmpdir" 178 | rm -rf "$mytmpdir" 179 | erase_previous_line 180 | 181 | if [ $param_zero_result -eq 1 ]; then 182 | exit_script 183 | fi 184 | fi 185 | } 186 | 187 | function display_applied_patches() { 188 | has_applied_patches=false 189 | has_updatable_patches=false 190 | for ((index=0; index<${#customization[@]}; index++)); do 191 | if [[ ${status[$index]} -eq 1 || ${status[$index]} -eq 2 ]]; then 192 | if [ "$has_applied_patches" = false ]; then 193 | echo -e "${INFO_FONT} Currently applied customizations:${NC}" 194 | has_applied_patches=true 195 | fi 196 | if [[ ${status[$index]} -eq 2 ]]; then 197 | echo -e " * ${customization[$index]} ${SUCCESS_FONT}(Update available)${NC}" 198 | has_updatable_patches=true 199 | else 200 | echo " * ${customization[$index]}" 201 | fi 202 | fi 203 | done 204 | if [ "$has_applied_patches" = true ]; then 205 | echo 206 | fi 207 | } 208 | 209 | function display_unapplicable_patches() { 210 | has_unapplicable_patches=false 211 | for ((index=0; index<${#customization[@]}; index++)); do 212 | if [ ${status[$index]} -eq 3 ]; then 213 | if [ "$has_unapplicable_patches" = false ]; then 214 | echo -e "${INFO_FONT} Unavailable customizations (due to conflicts):${NC}" 215 | has_unapplicable_patches=true 216 | fi 217 | echo " * ${customization[$index]}" 218 | fi 219 | done 220 | if [ "$has_unapplicable_patches" = true ]; then 221 | message_incompatible 222 | echo 223 | fi 224 | } 225 | 226 | function apply_patch { 227 | local index=$1 228 | local patch_file="${patch[$index]}" 229 | local customization_name="${customization[$index]}" 230 | local final_message_text="${final_message[$index]}" 231 | 232 | if [ -f "$patch_file" ]; then 233 | if [ -n "$final_message_text" ]; then 234 | section_divider 235 | echo -e "${final_message_text}" 236 | return_when_ready 237 | fi 238 | 239 | if git apply --whitespace=nowarn "$patch_file"; then 240 | echo -e "${SUCCESS_FONT} Customization $customization_name applied successfully${NC}" 241 | if [ "${clean_build[$index]}" == "1" ]; then 242 | echo -e "${INFO_FONT} Cleaning build folder, please wait ... patiently ...${NC}" 243 | xcodebuild -quiet -workspace "${workingdir}/LoopWorkspace.xcworkspace" -scheme LoopWorkspace clean 2>/dev/null 244 | fi 245 | sleep $SLEEP_TIME_AFTER_SUCCESS 246 | else 247 | echo -e "${ERROR_FONT} Failed to apply customization $customization_name${NC}" 248 | return_when_ready 249 | fi 250 | else 251 | echo -e "${ERROR_FONT} Patch file for customization $customization_name not available${NC}" 252 | return_when_ready 253 | fi 254 | refresh_status 255 | } 256 | 257 | function apply_patch_command_line { 258 | local index=$1 259 | local patch_file="${patch[$index]}" 260 | local customization_name="${customization[$index]}" 261 | if [ -f "$patch_file" ]; then 262 | if git apply --whitespace=nowarn "$patch_file"; then 263 | echo -e "${SUCCESS_FONT} Customization $customization_name applied successfully${NC}" 264 | else 265 | echo -e "${ERROR_FONT} Failed to apply customization $customization_name${NC}" 266 | exit 1 267 | fi 268 | else 269 | echo -e "${ERROR_FONT} Patch file for customization $customization_name not available${NC}" 270 | exit 1 271 | fi 272 | refresh_status 273 | } 274 | 275 | function revert_patch { 276 | local index=$1 277 | local patch_file="${patch[$index]}" 278 | local customization_name="${customization[$index]}" 279 | local final_message_text="${final_message[$index]}" 280 | 281 | if [ -f "$patch_file" ]; then 282 | if [ -n "$final_message_text" ]; then 283 | section_divider 284 | echo -e "$final_message_text" 285 | return_when_ready 286 | fi 287 | 288 | if git apply --whitespace=nowarn --reverse "$patch_file"; then 289 | echo -e "${SUCCESS_FONT} Customization $customization_name reverted successfully${NC}" 290 | if [ "${clean_build[$index]}" == "1" ]; then 291 | echo -e "${INFO_FONT} Cleaning build folder, please wait ... patiently ...${NC}" 292 | xcodebuild -quiet -workspace "${workingdir}/LoopWorkspace.xcworkspace" -scheme LoopWorkspace clean 2>/dev/null 293 | fi 294 | sleep $SLEEP_TIME_AFTER_SUCCESS 295 | else 296 | echo -e "${ERROR_FONT} Failed to revert customization $customization_name${NC}" 297 | return_when_ready 298 | fi 299 | else 300 | echo -e "${ERROR_FONT} Patch file for customization $customization_name does not exist${NC}" 301 | return_when_ready 302 | fi 303 | refresh_status 304 | } 305 | 306 | function download_patches { 307 | echo "Creating temporary folder" 308 | workingdir=$PWD 309 | mytmpdir=$(mktemp -d) 310 | 311 | # Check if tmp dir was created 312 | if [[ ! "$mytmpdir" || ! -d "$mytmpdir" ]]; then 313 | echo "Could not create temporary folder" 314 | exit 1 315 | fi 316 | erase_previous_line 317 | 318 | # Register the cleanup function to be called on the EXIT signal 319 | trap cleanup EXIT 320 | 321 | if [ -z "$LOCAL_PATCH_FOLDER" ]; then 322 | echo -e "${INFO_FONT}Downloading customizations, please wait ... patiently ...${NC}" 323 | cd $mytmpdir 324 | git clone --quiet --branch=$PATCH_BRANCH $PATCH_REPO 325 | clone_exit_status=$? 326 | if [ $clone_exit_status -eq 0 ]; then 327 | erase_previous_line 328 | cd $workingdir 329 | else 330 | echo -e "❌ ${ERROR_FONT}An error occurred during download. Please investigate the issue.${NC}" 331 | exit 1 332 | fi 333 | fi 334 | 335 | refresh_status 336 | if [ $CUSTOMIZATION_DEBUG -eq 1 ]; then 337 | debug_printout 338 | fi 339 | } 340 | 341 | function patch_menu { 342 | section_separator 343 | echo -e "${INFO_FONT}${app_name} Customization Select Script${NC}" 344 | 345 | cd "$STARTING_DIR" 346 | 347 | if [ "$(basename "$PWD")" != "${app_folder_name}" ]; then 348 | target_dir=$(find ${BUILD_DIR/#\~/$HOME} -maxdepth 1 -type d -name "${app_name}*" -exec [ -d "{}"/${app_folder_name} ] \; -print 2>/dev/null | xargs -I {} stat -f "%m %N" {} | sort -rn | head -n 1 | awk -v app_folder="${app_folder_name}" '{print $2"/"app_folder}') 349 | if [ -z "$target_dir" ]; then 350 | echo -e "${ERROR_FONT}Error: No folder containing ${app_folder_name} found in${NC}" 351 | echo " $BUILD_DIR" 352 | else 353 | cd "$target_dir" 354 | fi 355 | fi 356 | 357 | # Verify current folder 358 | if [ "$(basename "$PWD")" = "${app_folder_name}" ]; then 359 | download_patches 360 | echo 361 | 362 | ### repeating menu start here: 363 | while true; do 364 | echo -e "${INFO_FONT}Directory where customizations will be applied:${NC}" 365 | echo -e "${INFO_FONT} ${workingdir/$HOME/~}${NC}" 366 | echo 367 | 368 | display_applied_patches 369 | display_unapplicable_patches 370 | 371 | message_generic 372 | 373 | for ((index=0; index<${#customization[@]}; index++)); do 374 | if [ ${status[$index]} -eq 0 ]; then 375 | printf "%4d) %s\n" $((index+1)) "${customization[$index]}" 376 | if [ -n "${message_function[$index]}" ]; then 377 | eval "${message_function[$index]}" 378 | fi 379 | fi 380 | done 381 | 382 | echo -e "--------------------------------" 383 | 384 | if [ "$has_applied_patches" = true ]; then 385 | echo " $REMOVE_CUSTOMIZATION_MENU_ITEM) Remove a customization" 386 | fi 387 | if [ "$has_updatable_patches" = true ]; then 388 | echo -e "${SUCCESS_FONT} $UPDATE_CUSTOMIZATION_MENU_ITEM) Update a customization${NC}" 389 | fi 390 | 391 | echo " $EXIT_MENU_ITEM) $(exit_or_return_menu)" 392 | echo " $EXIT_OPEN_XCODE_MENU_ITEM) $(exit_or_return_menu) and open Xcode" 393 | echo 394 | 395 | if [ $one_time_flag -eq 0 ]; then 396 | message_about_display 397 | fi 398 | if [ $warning_flag -eq 0 ]; then 399 | warning_message 400 | fi 401 | read -p "Enter your choice: " choice 402 | if [[ $choice =~ ^[0-9]+$ && $choice -ge 1 && $choice -le $MAX_MENU_ITEM ]]; then 403 | if [[ $choice -le ${#customization[@]} ]]; then 404 | index=$(($choice-1)) 405 | if [ ${status[$index]} -eq 0 ]; then 406 | apply_patch "$index"; 407 | else 408 | echo -e "${ERROR_FONT}Your selection of $choice is not valid${NC}" 409 | return_when_ready 410 | fi 411 | elif [[ $choice -eq $REMOVE_CUSTOMIZATION_MENU_ITEM ]]; then 412 | section_separator 413 | echo -e "${INFO_FONT}Select a customization to remove:${NC}" 414 | 415 | for ((index=0; index<${#customization[@]}; index++)); do 416 | if [ ${status[$index]} -eq 1 ]; then 417 | echo "$((${index}+1))) ${customization[$index]}" 418 | fi 419 | done 420 | 421 | echo "*) Any other key will exit to last menu" 422 | read -p "Enter your choice: " choice 423 | if [[ $choice =~ ^[0-9]+$ && $choice -ge 1 && $choice -le ${#customization[@]} ]]; then 424 | index=$(($choice-1)) 425 | if [ ${status[$index]} -eq 1 ]; then 426 | revert_patch "$index"; 427 | else 428 | echo -e "${ERROR_FONT}Your selection of $choice is not valid${NC}" 429 | return_when_ready 430 | fi 431 | fi 432 | elif [[ $choice -eq $UPDATE_CUSTOMIZATION_MENU_ITEM ]]; then 433 | section_separator 434 | echo -e "${INFO_FONT}Select a customization to update:${NC}" 435 | 436 | for ((index=0; index<${#customization[@]}; index++)); do 437 | if [ ${status[$index]} -eq 2 ]; then 438 | echo "$((${index}+1))) ${customization[$index]}" 439 | fi 440 | done 441 | 442 | echo "*) Any other key will exit to last menu" 443 | read -p "Enter your choice: " choice 444 | if [[ $choice =~ ^[0-9]+$ && $choice -ge 1 && $choice -le ${#customization[@]} ]]; then 445 | index=$(($choice-1)) 446 | if [ ${status[$index]} -eq 2 ]; then 447 | echo "First reverse older version" 448 | revert_patch "$index"; 449 | echo "Now apply newer version" 450 | apply_patch "$index"; 451 | return_when_ready 452 | else 453 | echo -e "${ERROR_FONT}Your selection of $choice is not valid${NC}" 454 | return_when_ready 455 | fi 456 | fi 457 | elif [[ $choice -eq $EXIT_MENU_ITEM ]]; then 458 | exit 0 459 | elif [[ $choice -eq $EXIT_OPEN_XCODE_MENU_ITEM ]]; then 460 | echo -e "${INFO_FONT}Starting Xcode, please wait...${NC}" 461 | xed . 462 | exit 0 463 | else 464 | echo -e "${ERROR_FONT}Your choice of $choice is invalid${NC}" 465 | return_when_ready 466 | fi 467 | else 468 | echo 469 | echo -e "${ERROR_FONT}Your choice of $choice is invalid${NC}" 470 | return_when_ready 471 | fi 472 | section_separator 473 | done 474 | else 475 | exit 1 476 | fi 477 | } 478 | 479 | function patch_command_line { 480 | section_separator 481 | echo -e "${INFO_FONT}${app_name} Customization Select Script${NC}" 482 | 483 | cd "$STARTING_DIR" 484 | 485 | if [ "$(basename "$PWD")" != "${app_folder_name}" ]; then 486 | target_dir=$(find ${BUILD_DIR/#\~/$HOME} -maxdepth 1 -type d -name "${app_name}*" -exec [ -d "{}"/${app_folder_name} ] \; -print 2>/dev/null | xargs -I {} stat -f "%m %N" {} | sort -rn | head -n 1 | awk '{print $2"/${app_folder_name}"}') 487 | if [ -z "$target_dir" ]; then 488 | echo -e "${ERROR_FONT}Error: No folder containing ${app_folder_name} found in${NC}" 489 | echo " $BUILD_DIR" 490 | exit 1 491 | else 492 | cd "$target_dir" 493 | fi 494 | fi 495 | 496 | # Verify current folder 497 | if [ "$(basename "$PWD")" = "${app_folder_name}" ]; then 498 | download_patches 499 | echo -e "${INFO_FONT}Directory where customizations will be applied:${NC}" 500 | echo -e "${INFO_FONT} ${workingdir/$HOME/~}${NC}" 501 | echo 502 | 503 | for arg in "$@" 504 | do 505 | arg=$(translate_folder "$arg") 506 | 507 | found=false 508 | for i in "${!folder[@]}" 509 | do 510 | if [[ "${folder[$i]}" == "$arg" ]] 511 | then 512 | apply_patch_command_line "$i" 513 | found=true 514 | break 515 | fi 516 | done 517 | 518 | if ! $found 519 | then 520 | echo -e "${ERROR_FONT} Unknown customization $arg${NC}" 521 | exit 1 522 | fi 523 | done 524 | else 525 | exit 1 526 | fi 527 | } -------------------------------------------------------------------------------- /patch_cto/add_ab_ramp_option_LoopWorkspace_dev_0493004.patch: -------------------------------------------------------------------------------- 1 | Submodule Loop 7ce092f..c60bb40: 2 | diff --git a/Loop/Loop/Managers/LoopDataManager.swift b/Loop/Loop/Managers/LoopDataManager.swift 3 | index ffc66ee3..2d2f1ded 100644 4 | --- a/Loop/Loop/Managers/LoopDataManager.swift 5 | +++ b/Loop/Loop/Managers/LoopDataManager.swift 6 | @@ -67,6 +67,21 @@ final class LoopDataManager { 7 | 8 | private var insulinOnBoard: InsulinValue? 9 | 10 | + // Calculate the effectiveBolusApplicationFactor 11 | + private func calculateEffectiveBolusApplicationFactor(glucose: HKQuantity, correctionRangeSchedule: GlucoseRangeSchedule, settings: LoopSettings) -> Double { 12 | + // Calculate current glucose and lower bound target 13 | + let currentGlucose = glucose.doubleValue(for: .milligramsPerDeciliter) 14 | + let correctionRange = correctionRangeSchedule.quantityRange(at: now()) 15 | + let lowerBoundTarget = correctionRange.lowerBound.doubleValue(for: .milligramsPerDeciliter) 16 | + // Calculate minimum glucose sliding scale and scaling fraction 17 | + let minGlucoseSlidingScale = LoopConstants.minGlucoseDeltaSlidingScale + lowerBoundTarget 18 | + let scalingFraction = (LoopConstants.maxPartialApplicationFactor-LoopConstants.minPartialApplicationFactor) / (LoopConstants.maxGlucoseSlidingScale - minGlucoseSlidingScale) 19 | + let scalingGlucose = max(currentGlucose - minGlucoseSlidingScale, 0.0) 20 | + // Calculate effectiveBolusApplicationFactor 21 | + let effectiveBolusApplicationFactor = min(LoopConstants.minPartialApplicationFactor + scalingGlucose * scalingFraction, LoopConstants.maxPartialApplicationFactor) 22 | + return effectiveBolusApplicationFactor 23 | + } 24 | + 25 | deinit { 26 | for observer in notificationObservers { 27 | NotificationCenter.default.removeObserver(observer) 28 | @@ -1698,7 +1713,18 @@ extension LoopDataManager { 29 | return self.delegate?.roundBolusVolume(units: units) ?? units 30 | } 31 | 32 | - let maxAutomaticBolus = min(iobHeadroom, maxBolus! * LoopConstants.bolusPartialApplicationFactor) 33 | + var effectiveBolusApplicationFactor = LoopConstants.bolusPartialApplicationFactor 34 | + 35 | + let flagSlidingScale = settings.applyLinearRampToBolusApplicationFactor 36 | + 37 | + if flagSlidingScale { 38 | + let correctionRangeSchedule = settings.effectiveGlucoseTargetRangeSchedule() 39 | + effectiveBolusApplicationFactor = calculateEffectiveBolusApplicationFactor(glucose: glucose.quantity, correctionRangeSchedule: correctionRangeSchedule!, settings: settings) 40 | + } 41 | + print(" *** Glucose, effectiveBolusApplicationFactor: ", glucose.quantity, Double(Int(100.0*effectiveBolusApplicationFactor))/100.0) 42 | + 43 | + // If a user customizes maxPartialApplicationFactor > 1; this respects maxBolus 44 | + let maxAutomaticBolus = min(iobHeadroom, maxBolus! * min(effectiveBolusApplicationFactor, 1.0)) 45 | 46 | dosingRecommendation = predictedGlucose.recommendedAutomaticDose( 47 | to: glucoseTargetRange!, 48 | @@ -1708,7 +1734,7 @@ extension LoopDataManager { 49 | model: doseStore.insulinModelProvider.model(for: pumpInsulinType), 50 | basalRates: basalRateSchedule!, 51 | maxAutomaticBolus: maxAutomaticBolus, 52 | - partialApplicationFactor: LoopConstants.bolusPartialApplicationFactor * self.timeBasedDoseApplicationFactor, 53 | + partialApplicationFactor: effectiveBolusApplicationFactor * self.timeBasedDoseApplicationFactor, 54 | lastTempBasal: lastTempBasal, 55 | volumeRounder: volumeRounder, 56 | rateRounder: rateRounder, 57 | diff --git a/Loop/Loop/Managers/SettingsManager.swift b/Loop/Loop/Managers/SettingsManager.swift 58 | index e52cada0..ded3e7fc 100644 59 | --- a/Loop/Loop/Managers/SettingsManager.swift 60 | +++ b/Loop/Loop/Managers/SettingsManager.swift 61 | @@ -109,6 +109,7 @@ class SettingsManager { 62 | maximumBolus: latestSettings.maximumBolus, 63 | suspendThreshold: latestSettings.suspendThreshold, 64 | automaticDosingStrategy: latestSettings.automaticDosingStrategy, 65 | + applyLinearRampToBolusApplicationFactor: latestSettings.applyLinearRampToBolusApplicationFactor, 66 | defaultRapidActingModel: latestSettings.defaultRapidActingModel?.presetForRapidActingInsulin) 67 | } 68 | } 69 | @@ -140,7 +141,8 @@ class SettingsManager { 70 | cgmDevice: deviceStatusProvider?.cgmManagerStatus?.device, 71 | pumpDevice: deviceStatusProvider?.pumpManagerStatus?.device, 72 | bloodGlucoseUnit: displayGlucoseUnitObservable?.displayGlucoseUnit, 73 | - automaticDosingStrategy: newLoopSettings.automaticDosingStrategy) 74 | + automaticDosingStrategy: newLoopSettings.automaticDosingStrategy, 75 | + applyLinearRampToBolusApplicationFactor: newLoopSettings.applyLinearRampToBolusApplicationFactor) 76 | } 77 | 78 | func storeSettings(newLoopSettings: LoopSettings? = nil, notificationSettings: NotificationSettings? = nil) { 79 | diff --git a/Loop/Loop/Models/LoopConstants.swift b/Loop/Loop/Models/LoopConstants.swift 80 | index a62fc138..2dc64f18 100644 81 | --- a/Loop/Loop/Models/LoopConstants.swift 82 | +++ b/Loop/Loop/Models/LoopConstants.swift 83 | @@ -75,4 +75,12 @@ enum LoopConstants { 84 | static let simpleBolusCalculatorMinGlucoseBolusRecommendation = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 70) 85 | static let simpleBolusCalculatorMinGlucoseMealBolusRecommendation = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 55) 86 | static let simpleBolusCalculatorGlucoseWarningLimit = HKQuantity(unit: .milligramsPerDeciliter, doubleValue: 70) 87 | + 88 | + // Dosing Stategy: Automatic Bolus: Sliding Scale 89 | + static let minPartialApplicationFactor = 0.20 // min fraction of correction when glucose > minGlucoseSlidingScale 90 | + static let maxPartialApplicationFactor = 0.80 // max fraction of correction when glucose > maxGlucoseSlidingScale 91 | + // set minGlucoseSlidingScale based on user setting for correction range 92 | + // use mg/dL for calculations 93 | + static let minGlucoseDeltaSlidingScale = 10.0 // mg/dL 94 | + static let maxGlucoseSlidingScale = 200.0 // mg/dL 95 | } 96 | diff --git a/Loop/Loop/View Controllers/StatusTableViewController.swift b/Loop/Loop/View Controllers/StatusTableViewController.swift 97 | index 0af4bcea..48e62098 100644 98 | --- a/Loop/Loop/View Controllers/StatusTableViewController.swift 99 | +++ b/Loop/Loop/View Controllers/StatusTableViewController.swift 100 | @@ -1546,6 +1546,7 @@ final class StatusTableViewController: LoopChartsTableViewController { 101 | isClosedLoopAllowed: automaticDosingStatus.$isAutomaticDosingAllowed, 102 | supportInfoProvider: deviceManager, 103 | automaticDosingStrategy: deviceManager.loopManager.settings.automaticDosingStrategy, 104 | + applyLinearRampToBolusApplicationFactor: deviceManager.loopManager.settings.applyLinearRampToBolusApplicationFactor, 105 | availableSupports: supportManager.availableSupports, 106 | isOnboardingComplete: onboardingManager.isComplete, 107 | therapySettingsViewModelDelegate: deviceManager, 108 | @@ -2148,6 +2149,12 @@ extension StatusTableViewController: SettingsViewModelDelegate { 109 | } 110 | } 111 | 112 | + func applyLinearRampToBolusApplicationFactorChanged(_ value: Bool) { 113 | + self.deviceManager.loopManager.mutateSettings { settings in 114 | + settings.applyLinearRampToBolusApplicationFactor = value 115 | + } 116 | + } 117 | + 118 | func didTapIssueReport(title: String) { 119 | // TODO: this dismiss here is temporary, until we know exactly where 120 | // we want this screen to belong in the navigation flow 121 | diff --git a/Loop/Loop/View Models/SettingsViewModel.swift b/Loop/Loop/View Models/SettingsViewModel.swift 122 | index c24876e2..762c0ccf 100644 123 | --- a/Loop/Loop/View Models/SettingsViewModel.swift 124 | +++ b/Loop/Loop/View Models/SettingsViewModel.swift 125 | @@ -52,6 +52,7 @@ public typealias PumpManagerViewModel = DeviceViewModel 126 | public protocol SettingsViewModelDelegate: AnyObject { 127 | func dosingEnabledChanged(_: Bool) 128 | func dosingStrategyChanged(_: AutomaticDosingStrategy) 129 | + func applyLinearRampToBolusApplicationFactorChanged(_: Bool) 130 | func didTapIssueReport(title: String) 131 | var closedLoopDescriptiveText: String? { get } 132 | } 133 | @@ -94,6 +95,12 @@ public class SettingsViewModel: ObservableObject { 134 | } 135 | } 136 | 137 | + @Published var applyLinearRampToBolusApplicationFactor: Bool { 138 | + didSet { 139 | + delegate?.applyLinearRampToBolusApplicationFactorChanged(applyLinearRampToBolusApplicationFactor) 140 | + } 141 | + } 142 | + 143 | var closedLoopPreference: Bool { 144 | didSet { 145 | delegate?.dosingEnabledChanged(closedLoopPreference) 146 | @@ -115,6 +122,7 @@ public class SettingsViewModel: ObservableObject { 147 | isClosedLoopAllowed: Published.Publisher, 148 | supportInfoProvider: SupportInfoProvider, 149 | automaticDosingStrategy: AutomaticDosingStrategy, 150 | + applyLinearRampToBolusApplicationFactor: Bool, 151 | availableSupports: [SupportUI], 152 | isOnboardingComplete: Bool, 153 | therapySettingsViewModelDelegate: TherapySettingsViewModelDelegate?, 154 | @@ -132,6 +140,7 @@ public class SettingsViewModel: ObservableObject { 155 | self.closedLoopPreference = initialDosingEnabled 156 | self.isClosedLoopAllowed = false 157 | self.automaticDosingStrategy = automaticDosingStrategy 158 | + self.applyLinearRampToBolusApplicationFactor = applyLinearRampToBolusApplicationFactor 159 | self.supportInfoProvider = supportInfoProvider 160 | self.availableSupports = availableSupports 161 | self.isOnboardingComplete = isOnboardingComplete 162 | @@ -198,6 +207,7 @@ extension SettingsViewModel { 163 | isClosedLoopAllowed: FakeClosedLoopAllowedPublisher().$mockIsClosedLoopAllowed, 164 | supportInfoProvider: MockSupportInfoProvider(), 165 | automaticDosingStrategy: .automaticBolus, 166 | + applyLinearRampToBolusApplicationFactor: false, 167 | availableSupports: [], 168 | isOnboardingComplete: false, 169 | therapySettingsViewModelDelegate: nil, 170 | diff --git a/Loop/Loop/Views/DosingStrategySelectionView.swift b/Loop/Loop/Views/DosingStrategySelectionView.swift 171 | index f447b032..97ff6a32 100644 172 | --- a/Loop/Loop/Views/DosingStrategySelectionView.swift 173 | +++ b/Loop/Loop/Views/DosingStrategySelectionView.swift 174 | @@ -14,11 +14,13 @@ import LoopKitUI 175 | public struct DosingStrategySelectionView: View { 176 | 177 | @Binding private var automaticDosingStrategy: AutomaticDosingStrategy 178 | - 179 | + @Binding private var applyLinearRampToBolusApplicationFactor: Bool 180 | + 181 | @State private var internalDosingStrategy: AutomaticDosingStrategy 182 | 183 | - public init(automaticDosingStrategy: Binding) { 184 | + public init(automaticDosingStrategy: Binding, applyLinearRampToBolusApplicationFactor: Binding) { 185 | self._automaticDosingStrategy = automaticDosingStrategy 186 | + self._applyLinearRampToBolusApplicationFactor = applyLinearRampToBolusApplicationFactor 187 | self._internalDosingStrategy = State(initialValue: automaticDosingStrategy.wrappedValue) 188 | } 189 | 190 | @@ -45,13 +47,34 @@ public struct DosingStrategySelectionView: View { 191 | self.internalDosingStrategy = strategy // Hack to force update. :( 192 | } 193 | } 194 | - ) 195 | + ), 196 | + trailingView: strategy.isAutomaticBolus ? linearRampVolusApplicationFactorSection : nil 197 | ) 198 | .padding(.vertical, 4) 199 | } 200 | } 201 | } 202 | 203 | +extension DosingStrategySelectionView { 204 | + var linearRampVolusApplicationFactorSection: AnyView { 205 | + return AnyView( 206 | + Toggle(isOn: $applyLinearRampToBolusApplicationFactor) { 207 | + VStack(alignment: .leading) { 208 | + Text("Modify Bolus Percentage", comment: "The title text for the Modify Bolus Percentage toggle") 209 | + .padding(.vertical, 0.5) 210 | + Text("Modify Automatic Bolus behavior: The percentage of recommended bolus delivered each cycle varies with glucose level: near correction range, use 20% (similar to Temp Basal). Gradually increase to a maximum of 80% at high glucose (200 mg/dL, 11.1 mmol/L).", comment: "Description string for Modify Bolus Percentage toggle") 211 | + .font(.footnote) 212 | + .foregroundColor(.secondary) 213 | + .fixedSize(horizontal: false, vertical: true) 214 | + .multilineTextAlignment(.leading) 215 | + } 216 | + .fixedSize(horizontal: false, vertical: true) 217 | + } 218 | + .disabled(!automaticDosingStrategy.isAutomaticBolus) 219 | + ) 220 | + } 221 | +} 222 | + 223 | extension AutomaticDosingStrategy { 224 | var informationalText: String { 225 | switch self { 226 | @@ -62,10 +85,13 @@ extension AutomaticDosingStrategy { 227 | } 228 | } 229 | 230 | + var isAutomaticBolus: Bool { 231 | + return self == .automaticBolus 232 | + } 233 | } 234 | 235 | struct DosingStrategySelectionView_Previews: PreviewProvider { 236 | static var previews: some View { 237 | - DosingStrategySelectionView(automaticDosingStrategy: .constant(.automaticBolus)) 238 | + DosingStrategySelectionView(automaticDosingStrategy: .constant(.automaticBolus), applyLinearRampToBolusApplicationFactor: .constant(false)) 239 | } 240 | } 241 | diff --git a/Loop/Loop/Views/SettingsView.swift b/Loop/Loop/Views/SettingsView.swift 242 | index 22c10d48..71947324 100644 243 | --- a/Loop/Loop/Views/SettingsView.swift 244 | +++ b/Loop/Loop/Views/SettingsView.swift 245 | @@ -125,7 +125,7 @@ extension SettingsView { 246 | private var dosingStrategySection: some View { 247 | Section(header: SectionHeader(label: NSLocalizedString("Dosing Strategy", comment: "The title of the Dosing Strategy section in settings"))) { 248 | 249 | - NavigationLink(destination: DosingStrategySelectionView(automaticDosingStrategy: $viewModel.automaticDosingStrategy)) 250 | + NavigationLink(destination: DosingStrategySelectionView(automaticDosingStrategy: $viewModel.automaticDosingStrategy, applyLinearRampToBolusApplicationFactor: $viewModel.applyLinearRampToBolusApplicationFactor)) 251 | { 252 | HStack { 253 | Text(viewModel.automaticDosingStrategy.title) 254 | diff --git a/Loop/LoopCore/LoopSettings.swift b/Loop/LoopCore/LoopSettings.swift 255 | index 63418ad4..9dbbfcca 100644 256 | --- a/Loop/LoopCore/LoopSettings.swift 257 | +++ b/Loop/LoopCore/LoopSettings.swift 258 | @@ -73,6 +73,8 @@ public struct LoopSettings: Equatable { 259 | 260 | public var automaticDosingStrategy: AutomaticDosingStrategy = .tempBasalOnly 261 | 262 | + public var applyLinearRampToBolusApplicationFactor: Bool = false 263 | + 264 | public var defaultRapidActingModel: ExponentialInsulinModelPreset? 265 | 266 | public var glucoseUnit: HKUnit? { 267 | @@ -94,6 +96,7 @@ public struct LoopSettings: Equatable { 268 | maximumBolus: Double? = nil, 269 | suspendThreshold: GlucoseThreshold? = nil, 270 | automaticDosingStrategy: AutomaticDosingStrategy = .tempBasalOnly, 271 | + applyLinearRampToBolusApplicationFactor: Bool = false, 272 | defaultRapidActingModel: ExponentialInsulinModelPreset? = nil 273 | ) { 274 | self.dosingEnabled = dosingEnabled 275 | @@ -110,6 +113,7 @@ public struct LoopSettings: Equatable { 276 | self.maximumBolus = maximumBolus 277 | self.suspendThreshold = suspendThreshold 278 | self.automaticDosingStrategy = automaticDosingStrategy 279 | + self.applyLinearRampToBolusApplicationFactor = applyLinearRampToBolusApplicationFactor 280 | self.defaultRapidActingModel = defaultRapidActingModel 281 | } 282 | } 283 | @@ -277,13 +281,18 @@ extension LoopSettings: RawRepresentable { 284 | { 285 | self.automaticDosingStrategy = automaticDosingStrategy 286 | } 287 | + 288 | + if let applyLinearRampToBolusApplicationFactor = rawValue["applyLinearRampToBolusApplicationFactor"] as? Bool { 289 | + self.applyLinearRampToBolusApplicationFactor = applyLinearRampToBolusApplicationFactor 290 | + } 291 | } 292 | 293 | public var rawValue: RawValue { 294 | var raw: RawValue = [ 295 | "version": LoopSettings.version, 296 | "dosingEnabled": dosingEnabled, 297 | - "overridePresets": overridePresets.map { $0.rawValue } 298 | + "overridePresets": overridePresets.map { $0.rawValue }, 299 | + "applyLinearRampToBolusApplicationFactor": applyLinearRampToBolusApplicationFactor 300 | ] 301 | 302 | raw["glucoseTargetRangeSchedule"] = glucoseTargetRangeSchedule?.rawValue 303 | Submodule LoopKit a78aa8e..a572f2a: 304 | diff --git a/LoopKit/LoopKit/SettingsStore.swift b/LoopKit/LoopKit/SettingsStore.swift 305 | index afcb9d8..605b26b 100644 306 | --- a/LoopKit/LoopKit/SettingsStore.swift 307 | +++ b/LoopKit/LoopKit/SettingsStore.swift 308 | @@ -273,6 +273,7 @@ public struct StoredSettings: Equatable { 309 | // This is the user's display preference glucose unit. TODO: Rename? 310 | public let bloodGlucoseUnit: HKUnit? 311 | public let automaticDosingStrategy: AutomaticDosingStrategy 312 | + public let applyLinearRampToBolusApplicationFactor: Bool 313 | public let syncIdentifier: UUID 314 | 315 | public init(date: Date = Date(), 316 | @@ -299,6 +300,7 @@ public struct StoredSettings: Equatable { 317 | pumpDevice: HKDevice? = nil, 318 | bloodGlucoseUnit: HKUnit? = nil, 319 | automaticDosingStrategy: AutomaticDosingStrategy = .tempBasalOnly, 320 | + applyLinearRampToBolusApplicationFactor: Bool = false, 321 | syncIdentifier: UUID = UUID()) { 322 | self.date = date 323 | self.controllerTimeZone = controllerTimeZone 324 | @@ -324,6 +326,7 @@ public struct StoredSettings: Equatable { 325 | self.pumpDevice = pumpDevice 326 | self.bloodGlucoseUnit = bloodGlucoseUnit 327 | self.automaticDosingStrategy = automaticDosingStrategy 328 | + self.applyLinearRampToBolusApplicationFactor = applyLinearRampToBolusApplicationFactor 329 | self.syncIdentifier = syncIdentifier 330 | } 331 | } 332 | @@ -358,6 +361,7 @@ extension StoredSettings: Codable { 333 | pumpDevice: try container.decodeIfPresent(CodableDevice.self, forKey: .pumpDevice)?.device, 334 | bloodGlucoseUnit: bloodGlucoseUnit, 335 | automaticDosingStrategy: try container.decodeIfPresent(AutomaticDosingStrategy.self, forKey: .automaticDosingStrategy) ?? .tempBasalOnly, 336 | + applyLinearRampToBolusApplicationFactor: try container.decodeIfPresent(Bool.self, forKey: .applyLinearRampToBolusApplicationFactor) ?? false, 337 | syncIdentifier: try container.decode(UUID.self, forKey: .syncIdentifier)) 338 | } 339 | 340 | @@ -388,6 +392,7 @@ extension StoredSettings: Codable { 341 | try container.encodeIfPresent(pumpDevice.map { CodableDevice($0) }, forKey: .pumpDevice) 342 | try container.encode(bloodGlucoseUnit.unitString, forKey: .bloodGlucoseUnit) 343 | try container.encode(automaticDosingStrategy, forKey: .automaticDosingStrategy) 344 | + try container.encode(applyLinearRampToBolusApplicationFactor, forKey: .applyLinearRampToBolusApplicationFactor) 345 | try container.encode(syncIdentifier, forKey: .syncIdentifier) 346 | } 347 | 348 | @@ -432,6 +437,7 @@ extension StoredSettings: Codable { 349 | case pumpDevice 350 | case bloodGlucoseUnit 351 | case automaticDosingStrategy 352 | + case applyLinearRampToBolusApplicationFactor 353 | case syncIdentifier 354 | } 355 | } 356 | diff --git a/LoopKit/LoopKitUI/Views/CheckmarkListItem.swift b/LoopKit/LoopKitUI/Views/CheckmarkListItem.swift 357 | index 2c8b71f..f4aa877 100644 358 | --- a/LoopKit/LoopKitUI/Views/CheckmarkListItem.swift 359 | +++ b/LoopKit/LoopKitUI/Views/CheckmarkListItem.swift 360 | @@ -15,13 +15,15 @@ public struct CheckmarkListItem: View { 361 | var description: Text 362 | @Binding var isSelected: Bool 363 | let isEnabled: Bool 364 | + var trailingView: AnyView? = nil 365 | 366 | - public init(title: Text, titleFont: Font = .headline, description: Text, isSelected: Binding, isEnabled: Bool = true) { 367 | + public init(title: Text, titleFont: Font = .headline, description: Text, isSelected: Binding, isEnabled: Bool = true, trailingView: AnyView? = nil) { 368 | self.title = title 369 | self.titleFont = titleFont 370 | self.description = description 371 | self._isSelected = isSelected 372 | self.isEnabled = isEnabled 373 | + self.trailingView = trailingView 374 | } 375 | 376 | @ViewBuilder 377 | @@ -36,23 +38,29 @@ public struct CheckmarkListItem: View { 378 | } 379 | 380 | private var content: some View { 381 | - HStack(spacing: 0) { 382 | - VStack(alignment: .leading, spacing: 4) { 383 | - title 384 | - .font(titleFont) 385 | - description 386 | - .font(.footnote) 387 | - .foregroundColor(.secondary) 388 | - .fixedSize(horizontal: false, vertical: true) 389 | - .multilineTextAlignment(.leading) 390 | - } 391 | + VStack(alignment: .leading) { 392 | + HStack(spacing: 0) { 393 | + VStack(alignment: .leading, spacing: 4) { 394 | + title 395 | + .font(titleFont) 396 | + description 397 | + .font(.footnote) 398 | + .foregroundColor(.secondary) 399 | + .fixedSize(horizontal: false, vertical: true) 400 | + .multilineTextAlignment(.leading) 401 | + } 402 | 403 | - Spacer(minLength: 12) 404 | + Spacer(minLength: 12) 405 | 406 | - selectionIndicator 407 | - .accessibility(label: Text(isSelected ? "Selected" : "Unselected")) 408 | + selectionIndicator 409 | + .accessibility(label: Text(isSelected ? "Selected" : "Unselected")) 410 | + } 411 | + .animation(nil) 412 | + if let trailingView = trailingView { 413 | + trailingView 414 | + .padding(.top, 4) 415 | + } 416 | } 417 | - .animation(nil) 418 | } 419 | 420 | @ViewBuilder 421 | --------------------------------------------------------------------------------