From 91fc844600515ec4e8db17d298af53537a47b3f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jon=20B=20M=C3=A5rtensson?=
<53905247+Jon-b-m@users.noreply.github.com>
Date: Fri, 21 Apr 2023 14:35:21 +0200
Subject: [PATCH] Release 1.5.0 (#27)
* Hide target slider when ratio 1
* Filter out missing CGM readings from TIR (when older than 30 minutes)
* Watch App Colour matching. Now same colours as in iPhone app.
* Waiter's Notepad clean up and enumerate for Meal presets
* Meal presets. Several bug fixes.
* Profile override adaptive UI. Visualise when using extreme overrides. Improve override summation in confirmation alert.
* Overrides. Bug fixes.
* Info Panel updates.
Improve original TT format in info panel. Less dead space and less code.
Add % for TTs in info panel (currently only for presets or enacted with sliders).
Remove "Perpetual" to avoid clutter in info panel.
* Bug fixes for TTs.
* Remove delay for added variables, for instance for weighted average.
* Reduce time for compiling with code changes in HomeRootView. Will fix error when building in GitHub. Thank you, @antoinekh
* Use BackgroundContext again for APSManager...
* Bump version
---
Config.xcconfig | 2 +-
.../javascript/bundle/determine-basal.js | 2 +-
FreeAPS/Sources/APS/APSManager.swift | 231 +-----------------
FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift | 184 +++++++++++++-
FreeAPS/Sources/Models/Oref2_variables.swift | 4 +
.../Modules/AddCarbs/AddCarbsStateModel.swift | 90 ++++++-
.../AddCarbs/View/AddCarbsRootView.swift | 25 +-
.../AddTempTargetStateModel.swift | 28 ++-
.../View/AddTempTargetRootView.swift | 17 --
.../Sources/Modules/Home/HomeStateModel.swift | 11 +
.../Modules/Home/View/HomeRootView.swift | 145 ++++++-----
.../View/OverrideProfilesRootView.swift | 12 +-
.../Views/CarbsView.swift | 2 +-
.../Views/MainView.swift | 8 +-
14 files changed, 408 insertions(+), 353 deletions(-)
diff --git a/Config.xcconfig b/Config.xcconfig
index 86bcb4791a..f712ac6c31 100644
--- a/Config.xcconfig
+++ b/Config.xcconfig
@@ -1,5 +1,5 @@
APP_DISPLAY_NAME = iAPS
-APP_VERSION = 1.3.0
+APP_VERSION = 1.5.0
APP_BUILD_NUMBER = 1
COPYRIGHT_NOTICE =
DEVELOPER_TEAM = ##TEAM_ID##
diff --git a/FreeAPS/Resources/javascript/bundle/determine-basal.js b/FreeAPS/Resources/javascript/bundle/determine-basal.js
index 85d5a4ca7e..071417d2de 100644
--- a/FreeAPS/Resources/javascript/bundle/determine-basal.js
+++ b/FreeAPS/Resources/javascript/bundle/determine-basal.js
@@ -1 +1 @@
-var freeaps_determineBasal;(()=>{var e={5546:(e,t,a)=>{var r=a(6880);function o(e,t){t||(t=0);var a=Math.pow(10,t);return Math.round(e*a)/a}function n(e,t){return"mmol/L"===t.out_units?o(.0555*e,1):Math.round(e)}e.exports=function(e,t,a,i,s,l,m,u,d,c,g,h,p,f){var v=i.min_bg,B=0,b="",M="",_="",y="",x="",D=0,w=0,S=0,T=0,C=0,U=0;const G=f.weightedAverage;var O=1,R=i.sens,A=i.carb_ratio;f.useOverride&&(R/=O=f.overridePercentage/100,A/=O);const I=i.weightPercentage,j=f.average_total_data;function F(e,t){var a=e.getTime();return new Date(a+36e5*t)}function P(e){var t=i.bolus_increment;.025!=t&&(t=.05);var a=e/t;return a>=1?o(Math.floor(a)*t,5):0}function q(e){function t(e){return e<10&&(e="0"+e),e}return t(e.getHours())+":"+t(e.getMinutes())+":00"}function E(e,t){var a=new Date("1/1/1999 "+e),r=new Date("1/1/1999 "+t);return(a.getTime()-r.getTime())/36e5}function W(e,t){var a=0,r=t,o=(e-t)/36e5,n=0,i=o,s=0;do{if(o>0){var l=q(r),m=p[0].rate;for(let e=0;e
=(s=E(p[e+1].start,p[e].start))?n=s:o=(s=24-E(p[e].start,t))?n=s:ou)if(e+1=(s=E(d,l))?n=s:o=(s=E("23:59:59",l))?n=s:o0&&o21)C=W(k,(z=24-B,N=k.getTime(),new Date(N-36e5*z))),y="24 hours of data is required for an accurate tdd calculation. Currently only "+B.toPrecision(3)+" hours of pump history data are available. Using your pump scheduled basals to fill in the missing hours. Scheduled basals added: "+C.toPrecision(5)+" U. ";else B<21?(ae=!1,enableDynamicCR=!1):y=""}else console.log("Pumphistory is empty!"),ae=!1,enableDynamicCR=!1;var z,N;for(let e=0;e0){D=e,U=g[e].rate;var H=g[e-1]["duration (min)"]/60,Z=H,$=new Date(g[e-1].timestamp),J=$,K=0;do{if(e--,0==e){J=new Date;break}if("TempBasal"==g[e]._type||"PumpSuspend"==g[e]._type){J=new Date(g[e].timestamp);break}var Q=e-2;if(Q>=0&&"Rewind"==g[Q]._type){let e=g[Q].timestamp;for(;Q-1>=0&&"Prime"==g[Q-=1]._type;)K=(g[Q].timestamp-e)/36e5;K>=H&&(J=e,K=0)}}while(e>0);var V=(J-$)/36e5;V0&&(--r,"TempBasal"==g[r]._type)){a=new Date(g[r].timestamp);break}}while(r>0);(a-t)/36e5>0&&(C+=W(a,t))}for(let e=g.length-1;e>0;e--)if("TempBasalDuration"==g[e]._type){let t=g[e]["duration (min)"]/60,a=new Date(g[e].timestamp);var X=a;let r=e;do{if(--r,r>=0&&("TempBasal"==g[r]._type||"PumpSuspend"==g[r]._type)){X=new Date(g[r].timestamp);break}}while(r>0);if(0==e&&"TempBasalDuration"==g[0]._type&&(X=new Date,t=g[e]["duration (min)"]/60),(X-a)/36e5-t>0){C+=W(X,F(a,t))}}var Y,ee={TDD:o(w=T+S+C,5),bolus:o(T,5),temp_basal:o(S,5),scheduled_basal:o(C,5)};B>21?(M=". Bolus insulin: "+T.toPrecision(5)+" U",_=". Temporary basal insulin: "+S.toPrecision(5)+" U",b=". Insulin with scheduled basal rate: "+C.toPrecision(5)+" U",x=y+(" TDD past 24h is: "+w.toPrecision(5)+" U")+M+_+b,tddReason=", Total insulin: "+o(w,2)+" U, "+o(T/w*100,0)+"% Bolus "+o((S+C)/w*100,0)+"% Basal"):tddReason=", TDD: Not enough pumpData (< 21h)";const te=e.glucose;var ae=h.useNewFormula;const re=h.enableDynamicCR,oe=Math.min(i.autosens_min,i.autosens_max),ne=Math.max(i.autosens_min,i.autosens_max),ie=h.adjustmentFactor,se=i.min_bg;var le=!1,me="",ue=1,de="";j>0&&(ue=G/j),de=ue>1?"Basal adjustment with a 24 hour to total average (up to 14 days of data) TDD ratio (limited by Autosens max setting). Basal Ratio: "+(ue=o(ue=Math.min(ue,i.autosens_max),2))+". Upper limit = Autosens max ("+i.autosens_max+")":ue<1?"Basal adjustment with a 24 hour to to total average (up to 14 days of data) TDD ratio (limited by Autosens min setting). Basal Ratio: "+(ue=o(ue=Math.max(ue,i.autosens_min),2))+". Lower limit = Autosens min ("+i.autosens_min+")":"Basal adjusted with a 24 hour to total average (up to 14 days of data) TDD ratio: "+ue,de=", Basal ratio: "+ue,(i.high_temptarget_raises_sensitivity||i.exercise_mode||f.isEnabled)&&(le=!0),se>=118&&le&&(ae=!1,me="Dynamic ISF temporarily off due to a high temp target/exercising. Current min target: "+se);var ce=", Dynamic ratios log: ",ge=", AF: "+ie,he="BG: "+te+" mg/dl ("+(.0555*te).toPrecision(2)+" mmol/l)",pe="",fe="";const ve=h.curve,Be=h.insulinPeakTime,be=h.useCustomPeakTime;var Me=55,_e=65;switch(ve){case"rapid-acting":_e=65;break;case"ultra-rapid":_e=50}be?(Me=120-Be,console.log("Custom insulinpeakTime set to :"+Be+", insulinFactor: "+Me)):(Me=120-_e,console.log("insulinFactor set to : "+Me)),Y=w,I<1&&G>0&&(w=G,console.log("Using weighted TDD average: "+o(w,2)+" U, instead of past 24 h ("+o(Y,2)+" U), weight: "+I),fe=", Weighted TDD: "+o(w,2)+" U");const ye=h.sigmoid;var xe="";if(ae){var De=R*ie*w*Math.log(te/Me+1)/1800;pe=", Logarithmic formula"}if(ae&&ye){const e=oe,t=ne-e,a=.0555*(te-i.min_bg);var we=ue;const r=ne-1,o=Math.log10(1/r-e/r)/Math.log10(Math.E),n=a*ie*we+o;De=t/(1+Math.exp(-n))+e,pe=", Sigmoid function"}var Se=A;const Te=o(A,1);var Ce="",Ue="";if(ae&&w>0){if(Ce=", Dynamic ISF/CR: On/",De>ne?(me=", Dynamic ISF limited by autosens_max setting: "+ne+" ("+o(De,2)+"), ",Ue=", Autosens/Dynamic Limit: "+ne+" ("+o(De,2)+")",De=ne):De1&&(Ge=(De-1)/2+1);var Oe=" CR: "+(Se=o(Se/Ge,2))+" g/U";A=Se}else Oe=" CR: "+Se+" g/U",Ce+="Off";const e=R/De;xe=". Using Sigmoid function, the autosens ratio has been adjusted with sigmoid factor to: "+o(s.ratio,2)+". New ISF = "+o(e,2)+" mg/dl ("+o(.0555*e,2)+" (mmol/l). CR adjusted from "+o(Te,2)+" to "+o(Se,2)+" ("+o(.0555*A,2)+" mmol/l).",me+=ye?xe:", Dynamic autosens.ratio set to "+o(De,2)+" with ISF: "+e.toPrecision(3)+" mg/dl/U ("+(.0555*e).toPrecision(3)+" mmol/l/U)",s.ratio=De,x+=ce+he+ge+pe+me+Ce+Oe+fe}else x+=ce+"Dynamic Settings disabled";console.log(x),ae||re?ae&&i.tddAdjBasal?tddReason+=Ce+pe+Ue+ge+de:ae&&!i.tddAdjBasal&&(tddReason+=Ce+pe+Ue+ge):tddReason+="";var Re={},Ae=new Date;if(c&&(Ae=c),void 0===i||void 0===i.current_basal)return Re.error="Error: could not get current basal rate",Re;var Ie=r(i.current_basal,i)*O,je=Ie;f.useOverride&&(0==f.duration?console.log("Profile Override is active. Override "+o(100*O,0)+"%. Override Duration: Enabled indefinitely"):console.log("Profile Override is active. Override "+o(100*O,0)+"%. Override Expires in: "+f.duration+" min."));var Fe=new Date;c&&(Fe=c);var Pe,qe=new Date(e.date),Ee=o((Fe-qe)/60/1e3,1),We=e.glucose,ke=e.noise;Pe=e.delta>-.5?"+"+o(e.delta,0):o(e.delta,0);var Le=Math.min(e.delta,e.short_avgdelta),ze=Math.min(e.short_avgdelta,e.long_avgdelta),Ne=Math.max(e.delta,e.short_avgdelta,e.long_avgdelta);(We<=10||38===We||ke>=3)&&(Re.reason="CGM is calibrating, in ??? state, or noise is high");if(We>60&&0==e.delta&&e.short_avgdelta>-1&&e.short_avgdelta<1&&e.long_avgdelta>-1&&e.long_avgdelta<1&&("fakecgm"==e.device?(console.error("CGM data is unchanged ("+n(We,i)+"+"+n(e.delta,i)+") for 5m w/ "+n(e.short_avgdelta,i)+" mg/dL ~15m change & "+n(e.long_avgdelta,2)+" mg/dL ~45m change"),console.error("Simulator mode detected ("+e.device+"): continuing anyway")):!0),Ee>12||Ee<-5?Re.reason="If current system time "+Fe+" is correct, then BG data is too old. The last BG data was read "+Ee+"m ago at "+qe:0===e.short_avgdelta&&0===e.long_avgdelta&&(e.last_cal&&e.last_cal<3?Re.reason="CGM was just calibrated":Re.reason="CGM data is unchanged ("+n(We,i)+"+"+n(e.delta,i)+") for 5m w/ "+n(e.short_avgdelta,i)+" mg/dL ~15m change & "+n(e.long_avgdelta,i)+" mg/dL ~45m change"),We<=10||38===We||ke>=3||Ee>12||Ee<-5||0===e.short_avgdelta&&0===e.long_avgdelta)return t.rate>=je?(Re.reason+=". Canceling high temp basal of "+t.rate,Re.deliverAt=Ae,Re.temp="absolute",Re.duration=0,Re.rate=0,Re):0===t.rate&&t.duration>30?(Re.reason+=". Shortening "+t.duration+"m long zero temp to 30m. ",Re.deliverAt=Ae,Re.temp="absolute",Re.duration=30,Re.rate=0,Re):(Re.reason+=". Temp "+t.rate+" <= current basal "+je+"U/hr; doing nothing. ",Re);var He,Ze,$e,Je,Ke=i.max_iob;if(void 0!==i.min_bg&&(Ze=i.min_bg),void 0!==i.max_bg&&($e=i.max_bg),void 0!==i.enableSMB_high_bg_target&&(Je=i.enableSMB_high_bg_target),void 0===i.min_bg||void 0===i.max_bg)return Re.error="Error: could not determine target_bg. ",Re;He=(i.min_bg+i.max_bg)/2;var Qe,Ve=i.exercise_mode||i.high_temptarget_raises_sensitivity||f.isEnabled,Xe=100;if(Qe=i.half_basal_exercise_target,f.overridePercentage&&f.hbt){const e=f.hbt;console.log("Half Basal Target used: "+n(e,i)+" "+i.out_units)}else console.log("Default Half Basal Target used: "+n(Qe,i)+" "+i.out_units);if(Ve&&i.temptargetSet&&He>Xe||i.low_temptarget_lowers_sensitivity&&i.temptargetSet&&He=He&&sensitivityRatio0&&(process.stderr.write("TDD-adjustment of basals activated, using tdd24h_14d_Ratio "+o(ue,2)+", TDD 24h = "+o(Y,2)+"U, Weighted average TDD = "+o(G,2)+"U, (Weight percentage = "+I+"), Total data of TDDs (up to 14 days) average = "+o(j,2)+"U. "),je!==Ie*O?process.stderr.write("Adjusting basal from "+Ie*O+" U/h to "+je+" U/h; "):process.stderr.write("Basal unchanged: "+je+" U/h; "))),i.temptargetSet);else if(void 0!==s&&s&&(i.sensitivity_raises_target&&s.ratio<1||i.resistance_lowers_target&&s.ratio>1)){Ze=o((Ze-60)/s.ratio)+60,$e=o(($e-60)/s.ratio)+60;var et=o((He-60)/s.ratio)+60;He===(et=Math.max(80,et))?process.stderr.write("target_bg unchanged: "+et+"; "):process.stderr.write("target_bg from "+He+" to "+et+"; "),He=et}var tt=200,at=200,rt=200;if(e.noise>=2){var ot=Math.max(1.1,i.noisyCGMTargetMultiplier);Math.min(250,i.maxRaw);tt=o(Math.min(200,Ze*ot)),at=o(Math.min(200,He*ot)),rt=o(Math.min(200,$e*ot)),process.stderr.write("Raising target_bg for noisy / raw CGM data, from "+He+" to "+at+"; "),Ze=tt,He=at,$e=rt}var nt=Ze-.5*(Ze-40),it=i.threshold_setting;it>nt&&it<=120&&it>=65?(console.error("Threshold changed in settings from "+n(nt,i)+" to "+n(it,i)+". "),nt=it):console.error("Current threshold: "+n(nt,i));var st="",lt=(o(R,1),R);if(void 0!==s&&s&&((lt=o(lt=R/sensitivityRatio,1))!==R?process.stderr.write("ISF from "+n(R,i)+" to "+n(lt,i)):process.stderr.write("ISF unchanged: "+n(lt,i)),st+="Autosens ratio: "+o(sensitivityRatio,2)+", ISF: "+n(R,i)+"→"+n(lt,i)),console.error("CR:"+A),void 0===a)return Re.error="Error: iob_data undefined. ",Re;var mt,ut=a;if(a.length,a.length>1&&(a=ut[0]),void 0===a.activity||void 0===a.iob)return Re.error="Error: iob_data missing some property. ",Re;var dt=((mt=void 0!==a.lastTemp?o((new Date(Fe).getTime()-a.lastTemp.date)/6e4):0)+t.duration)%30;if(console.error("currenttemp:"+t.rate+" lastTempAge:"+mt+"m, tempModulus:"+dt+"m"),Re.temp="absolute",Re.deliverAt=Ae,u&&t&&a.lastTemp&&t.rate!==a.lastTemp.rate&&mt>10&&t.duration)return Re.reason="Warning: currenttemp rate "+t.rate+" != lastTemp rate "+a.lastTemp.rate+" from pumphistory; canceling temp",m.setTempBasal(0,0,i,Re,t);if(t&&a.lastTemp&&t.duration>0){var ct=mt-a.lastTemp.duration;if(ct>5&&mt>10)return Re.reason="Warning: currenttemp running but lastTemp from pumphistory ended "+ct+"m ago; canceling temp",m.setTempBasal(0,0,i,Re,t)}var gt=o(-a.activity*lt*5,2),ht=o(6*(Le-gt));ht<0&&(ht=o(6*(ze-gt)))<0&&(ht=o(6*(e.long_avgdelta-gt)));var pt=We,ft=(pt=a.iob>0?o(We-a.iob*lt):o(We-a.iob*Math.min(lt,R)))+ht;if(void 0===ft||isNaN(ft))return Re.error="Error: could not calculate eventualBG. Sensitivity: "+lt+" Deviation: "+ht,Re;var vt=function(e,t,a){return o(a+(e-t)/24,1)}(He,ft,gt);Re={temp:"absolute",bg:We,tick:Pe,eventualBG:ft,insulinReq:0,reservoir:d,deliverAt:Ae,sensitivityRatio,TDD:Y,insulin:ee,current_target:He};var Bt=[],bt=[],Mt=[],_t=[];Bt.push(We),bt.push(We),_t.push(We),Mt.push(We);var yt=function(e,t,a,r,o,i){return t?!e.allowSMB_with_high_temptarget&&e.temptargetSet&&o>100?(console.error("SMB disabled due to high temptarget of "+o),!1):!0===a.bwFound&&!1===e.A52_risk_enable?(console.error("SMB disabled due to Bolus Wizard activity in the last 6 hours."),!1):!0===e.enableSMB_always?(a.bwFound?console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled due to enableSMB_always"),!0):!0===e.enableSMB_with_COB&&a.mealCOB?(a.bwCarbs?console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for COB of "+a.mealCOB),!0):!0===e.enableSMB_after_carbs&&a.carbs?(a.bwCarbs?console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for 6h after carb entry"),!0):!0===e.enableSMB_with_temptarget&&e.temptargetSet&&o<100?(a.bwFound?console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for temptarget of "+n(o,e)),!0):!0===e.enableSMB_high_bg&&null!==i&&r>=i?(console.error("Checking BG to see if High for SMB enablement."),console.error("Current BG",r," | High BG ",i),a.bwFound?console.error("Warning: High BG SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("High BG detected. Enabling SMB."),!0):(console.error("SMB disabled (no enableSMB preferences active or no condition satisfied)"),!1):(console.error("SMB disabled (!microBolusAllowed)"),!1)}(i,u,l,We,He,Je),xt=i.enableUAM,Dt=0,wt=0;Dt=o(Le-gt,1);var St=o(Le-gt,1);csf=lt/A,console.error("profile.sens:"+n(R,i)+", sens:"+n(lt,i)+", CSF:"+o(csf,1));var Tt=o(30*csf*5/60,1);Dt>Tt&&(console.error("Limiting carb impact from "+Dt+" to "+Tt+"mg/dL/5m (30g/h)"),Dt=Tt);var Ct=3;sensitivityRatio&&(Ct/=sensitivityRatio);var Ut=Ct;if(l.carbs){Ct=Math.max(Ct,l.mealCOB/20);var Gt=o((new Date(Fe).getTime()-l.lastCarbTime)/6e4),Ot=(l.carbs-l.mealCOB)/l.carbs;Ut=o(Ut=Ct+1.5*Gt/60,1),console.error("Last carbs "+Gt+" minutes ago; remainingCATime:"+Ut+"hours; "+o(100*Ot,1)+"% carbs absorbed")}var Rt=Math.max(0,Dt/5*60*Ut/2)/csf,At=90,It=1;i.remainingCarbsCap&&(At=Math.min(90,i.remainingCarbsCap)),i.remainingCarbsFraction&&(It=Math.min(1,i.remainingCarbsFraction));var jt=1-It,Ft=Math.max(0,l.mealCOB-Rt-l.carbs*jt),Pt=(Ft=Math.min(At,Ft))*csf*5/60/(Ut/2),qt=o(l.slopeFromMaxDeviation,2),Et=o(l.slopeFromMinDeviation,2),Wt=Math.min(qt,-Et/3);wt=0===Dt?0:Math.min(60*Ut/5/2,Math.max(0,l.mealCOB*csf/Dt)),console.error("Carb Impact:"+Dt+"mg/dL per 5m; CI Duration:"+o(5*wt/60*2,1)+"hours; remaining CI ("+Ut/2+"h peak):"+o(Pt,1)+"mg/dL per 5m");var kt,Lt,zt,Nt,Ht,Zt=999,$t=999,Jt=999,Kt=We,Qt=999,Vt=999,Xt=999,Yt=999,ea=ft,ta=We,aa=We,ra=0,oa=[],na=[];try{ut.forEach((function(e){var t=o(-e.activity*lt*5,2),a=o(-e.iobWithZeroTemp.activity*lt*5,2),r=pt,n=Dt*(1-Math.min(1,bt.length/12));if(!0===(ae&&!ye))ea=bt[bt.length-1]+o(-e.activity*(1800/(w*ie*Math.log(Math.max(bt[bt.length-1],39)/Me+1)))*5,2)+n,r=_t[_t.length-1]+o(-e.iobWithZeroTemp.activity*(1800/(w*ie*Math.log(Math.max(_t[_t.length-1],39)/Me+1)))*5,2),console.log("Dynamic ISF (Logarithmic Formula) )adjusted predictions for IOB and ZT: IOBpredBG: "+o(ea,2)+" , ZTpredBG: "+o(r,2));else ea=bt[bt.length-1]+t+n,r=_t[_t.length-1]+a;var i=Math.max(0,Math.max(0,Dt)*(1-Bt.length/Math.max(2*wt,1))),s=Math.min(Bt.length,12*Ut-Bt.length),l=Math.max(0,s/(Ut/2*12)*Pt);i+l,oa.push(o(l,0)),na.push(o(i,0)),COBpredBG=Bt[Bt.length-1]+t+Math.min(0,n)+i+l;var m=Math.max(0,St+Mt.length*Wt),u=Math.max(0,St*(1-Mt.length/Math.max(36,1))),d=Math.min(m,u);if(d>0&&(ra=o(5*(Mt.length+1)/60,1)),!0===(ae&&!ye))UAMpredBG=Mt[Mt.length-1]+o(-e.activity*(1800/(w*ie*Math.log(Math.max(Mt[Mt.length-1],39)/Me+1)))*5,2)+Math.min(0,n)+d,console.log("Dynamic ISF (Logarithmic Formula) adjusted prediction for UAM: UAMpredBG: "+o(UAMpredBG,2));else UAMpredBG=Mt[Mt.length-1]+t+Math.min(0,n)+d;bt.length<48&&bt.push(ea),Bt.length<48&&Bt.push(COBpredBG),Mt.length<48&&Mt.push(UAMpredBG),_t.length<48&&_t.push(r),COBpredBG18&&eata&&(ta=ea),(wt||Pt>0)&&Bt.length>18&&COBpredBG<$t&&($t=o(COBpredBG)),(wt||Pt>0)&&COBpredBG>ta&&(aa=COBpredBG),xt&&Mt.length>12&&UAMpredBGta&&UAMpredBG}))}catch(e){console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled")}l.mealCOB&&(console.error("predCIs (mg/dL/5m):"+na.join(" ")),console.error("remainingCIs: "+oa.join(" "))),Re.predBGs={},bt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))}));for(var ia=bt.length-1;ia>12&&bt[ia-1]===bt[ia];ia--)bt.pop();for(Re.predBGs.IOB=bt,zt=o(bt[bt.length-1]),_t.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=_t.length-1;ia>6&&!(_t[ia-1]>=_t[ia]||_t[ia]<=He);ia--)_t.pop();if(Re.predBGs.ZT=_t,o(_t[_t.length-1]),l.mealCOB>0&&(Dt>0||Pt>0)){for(Bt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=Bt.length-1;ia>12&&Bt[ia-1]===Bt[ia];ia--)Bt.pop();Re.predBGs.COB=Bt,Nt=o(Bt[Bt.length-1]),ft=Math.max(ft,o(Bt[Bt.length-1]))}if(Dt>0||Pt>0){if(xt){for(Mt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=Mt.length-1;ia>12&&Mt[ia-1]===Mt[ia];ia--)Mt.pop();Re.predBGs.UAM=Mt,Ht=o(Mt[Mt.length-1]),Mt[Mt.length-1]&&(ft=Math.max(ft,o(Mt[Mt.length-1])))}Re.eventualBG=ft}console.error("UAM Impact:"+St+"mg/dL per 5m; UAM Duration:"+ra+"hours"),Zt=Math.max(39,Zt),$t=Math.max(39,$t),Jt=Math.max(39,Jt),kt=o(Zt);var sa=l.mealCOB/l.carbs;Lt=o(Jt<999&&$t<999?(1-sa)*UAMpredBG+sa*COBpredBG:$t<999?(ea+COBpredBG)/2:Jt<999?(ea+UAMpredBG)/2:ea),Yt>Lt&&(Lt=Yt),Kt=o(Kt=wt||Pt>0?xt?sa*Qt+(1-sa)*Vt:Qt:xt?Vt:Xt);var la=Jt;if(YtJt&&(la=(Jt+Yt)/2);if(la=o(la),l.carbs)if(!xt&&$t<999)kt=o(Math.max(Zt,$t));else if($t<999){var ua=sa*$t+(1-sa)*la;kt=o(Math.max(Zt,$t,ua))}else kt=xt?la:Kt;else xt&&(kt=o(Math.max(Zt,la)));kt=Math.min(kt,Lt),process.stderr.write("minPredBG: "+kt+" minIOBPredBG: "+Zt+" minZTGuardBG: "+Yt),$t<999&&process.stderr.write(" minCOBPredBG: "+$t),Jt<999&&process.stderr.write(" minUAMPredBG: "+Jt),console.error(" avgPredBG:"+Lt+" COB/Carbs:"+l.mealCOB+"/"+l.carbs),aa>We&&(kt=Math.min(kt,aa)),Re.COB=l.mealCOB,Re.IOB=a.iob,Re.BGI=n(gt,i),Re.deviation=n(ht,i),Re.ISF=n(lt,i),Re.CR=o(A,1),Re.target_bg=n(He,i),Re.TDD=o(Y,2),Re.current_target=o(He,0);var da=Re.CR;Te!=Re.CR&&(da=Te+"→"+Re.CR);var ca=Re.target_bg;He!=v&&(ca=n(v,i)+"→"+Re.target_bg),Re.reason=st+", COB: "+Re.COB+", Dev: "+Re.deviation+", BGI: "+Re.BGI+", CR: "+da+", Target: "+ca+", minPredBG "+n(kt,i)+", minGuardBG "+n(Kt,i)+", IOBpredBG "+n(zt,i),Nt>0&&(Re.reason+=", COBpredBG "+n(Nt,i)),Ht>0&&(Re.reason+=", UAMpredBG "+n(Ht,i)),Re.reason+=tddReason,Re.reason+="; ";var ga=pt;ga<40&&(ga=Math.min(Kt,ga));var ha,pa=nt-ga,fa=240,va=240;if(l.mealCOB>0&&(Dt>0||Pt>0)){for(ia=0;iaha*We&&(console.error("maxDelta "+n(Ne,i)+" > "+100*ha+"% of BG "+n(We,i)+" - disabling SMB"),Re.reason+="maxDelta "+n(Ne,i)+" > "+100*ha+"% of BG "+n(We,i)+" - SMB disabled!, ",yt=!1),console.error("BG projected to remain above "+n(Ze,i)+" for "+fa+"minutes"),(va<240||fa<60)&&console.error("BG projected to remain above "+n(nt,i)+" for "+va+"minutes");var Ba=va,ba=i.current_basal*O*lt*Ba/60,Ma=Math.max(0,l.mealCOB-.25*l.carbs),_a=(pa-ba)/csf-Ma;ba=o(ba),_a=o(_a),console.error("naive_eventualBG:",pt,"bgUndershoot:",pa,"zeroTempDuration:",Ba,"zeroTempEffect:",ba,"carbsReq:",_a),"Could not parse clock data"==l.reason?console.error("carbsReq unknown: Could not parse clock data"):_a>=i.carbsReqThreshold&&va<=45&&(Re.carbsReq=_a,Re.reason+=_a+" add'l carbs req w/in "+va+"m; ");var ya=0;if(We0&&Le>vt)Re.reason+="IOB "+a.iob+" < "+o(-i.current_basal*O*20/60,2),Re.reason+=" and minDelta "+n(Le,i)+" > expectedDelta "+n(vt,i)+"; ";else if(We=55)return Re.reason+="; Canceling temp at "+Re.deliverAt.getMinutes()+"m past the hour. ",m.setTempBasal(0,0,i,Re,t);var xa=0,Da=je;if(ftvt&&Le>0&&!_a)return pt<40?(Re.reason+=", naive_eventualBG < 40. ",m.setTempBasal(0,30,i,Re,t)):(e.delta>Le?Re.reason+=", but Delta "+n(Pe,i)+" > expectedDelta "+n(vt,i):Re.reason+=", but Min. Delta "+Le.toFixed(2)+" > Exp. Delta "+n(vt,i),t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t)));xa=o(xa=2*Math.min(0,(ft-He)/lt),2);var wa=Math.min(0,(pt-He)/lt);if(wa=o(wa,2),Le<0&&Le>vt)xa=o(xa*(Le/vt),2);if(Da=r(Da=je+2*xa,i),t.duration*(t.rate-je)/605&&Da>=.8*t.rate)return Re.reason+=", temp "+t.rate+" ~< req "+Da+"U/hr. ",Re;if(Da<=0){if((ya=o(60*((pa=He-pt)/lt)/i.current_basal*O))<0?ya=0:(ya=30*o(ya/30),ya=Math.min(120,Math.max(0,ya))),ya>0)return Re.reason+=", setting "+ya+"m zero temp. ",m.setTempBasal(Da,ya,i,Re,t)}else Re.reason+=", setting "+Da+"U/hr. ";return m.setTempBasal(Da,30,i,Re,t)}if(Le "+n(Ze,i)+" but Delta "+n(Pe,i)+" < Exp. Delta "+n(vt,i):Re.reason+="Eventual BG "+n(ft,i)+" > "+n(Ze,i)+" but Min. Delta "+Le.toFixed(2)+" < Exp. Delta "+n(vt,i),t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));if(Math.min(ft,kt)<$e&&(!u||!yt))return Re.reason+=n(ft,i)+"-"+n(kt,i)+" in range: no temp required",t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));if(ft>=$e&&(Re.reason+="Eventual BG "+n(ft,i)+" >= "+n($e,i)+", "),a.iob>Ke)return Re.reason+="IOB "+o(a.iob,2)+" > max_iob "+Ke,t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));(xa=o((Math.min(kt,ft)-He)/lt,2))>Ke-a.iob?(console.error("SMB limited by maxIOB: "+Ke-a.iob+" (. insulinReq: "+xa+" U)"),Re.reason+="max_iob "+Ke+", ",xa=Ke-a.iob):console.error("SMB not limited by maxIOB ( insulinReq: "+xa+" U)."),Da=r(Da=je+2*xa,i),xa=o(xa,3),Re.insulinReq=xa;var Sa=o((new Date(Fe).getTime()-a.lastBolusTime)/6e4,1);if(u&&yt&&We>nt){var Ta=o(l.mealCOB/A,3),Ca=0;void 0===i.maxSMBBasalMinutes?(Ca=o(i.current_basal*O*30/60,1),console.error("profile.maxSMBBasalMinutes undefined: defaulting to 30m"),xa>Ca&&console.error("SMB limited by maxBolus: "+Ca+" ( "+xa+" U)")):a.iob>Ta&&a.iob>0?(console.error("IOB"+a.iob+"> COB"+l.mealCOB+"; mealInsulinReq ="+Ta),i.maxUAMSMBBasalMinutes?(console.error("profile.maxUAMSMBBasalMinutes: "+i.maxUAMSMBBasalMinutes+", profile.current_basal: "+i.current_basal*O),Ca=o(i.current_basal*O*i.maxUAMSMBBasalMinutes/60,1)):(console.error("profile.maxUAMSMBBasalMinutes undefined: defaulting to 30m"),Ca=o(i.current_basal*O*30/60,1)),xa>Ca?console.error("SMB limited by maxUAMSMBBasalMinutes [ "+i.maxUAMSMBBasalMinutes+"m ]: "+Ca+"U ( "+xa+"U )"):console.error("SMB is not limited by maxUAMSMBBasalMinutes. ( insulinReq: "+xa+"U )")):(console.error("profile.maxSMBBasalMinutes: "+i.maxSMBBasalMinutes+", profile.current_basal: "+i.current_basal*O),xa>(Ca=o(i.current_basal*i.maxSMBBasalMinutes/60,1))?console.error("SMB limited by maxSMBBasalMinutes: "+i.maxSMBBasalMinutes+"m ]: "+Ca+"U ( insulinReq: "+xa+"U )"):console.error("SMB is not limited by maxSMBBasalMinutes. ( insulinReq: "+xa+"U )"));var Ua=i.bolus_increment,Ga=1/Ua,Oa=i.smb_delivery_ratio;Oa>.5&&console.error("SMB Delivery Ratio increased from default 0.5 to "+o(Oa,2));var Ra=Math.min(xa*Oa,Ca);Ra=Math.floor(Ra*Ga)/Ga,ya=o(60*((He-(pt+Zt)/2)/lt)/i.current_basal*O),xa>0&&Ra=30?(ya=30*o(ya/30),ya=Math.min(60,Math.max(0,ya))):(Aa=o(je*ya/30,2),ya=30),Re.reason+=" insulinReq "+xa,Ra>=Ca&&(Re.reason+="; maxBolus "+Ca),ya>0&&(Re.reason+="; setting "+ya+"m low temp of "+Aa+"U/h"),Re.reason+=". ";var Ia=3;i.SMBInterval&&(Ia=Math.min(10,Math.max(1,i.SMBInterval)));var ja=o(Ia-Sa,0),Fa=o(60*(Ia-Sa),0)%60;if(console.error("naive_eventualBG "+pt+","+ya+"m "+Aa+"U/h temp needed; last bolus "+Sa+"m ago; maxBolus: "+Ca),Sa>Ia?Ra>0&&(Re.units=Ra,Re.reason+="Microbolusing "+Ra+"U. "):Re.reason+="Waiting "+ja+"m "+Fa+"s to microbolus again. ",ya>0)return Re.rate=Aa,Re.duration=ya,Re}var Pa=m.getMaxSafeBasal(i);return Da>Pa&&(Re.reason+="adj. req. rate: "+Da+" to maxSafeBasal: "+o(Pa,2)+", ",Da=r(Pa,i)),t.duration*(t.rate-je)/60>=2*xa?(Re.reason+=t.duration+"m@"+t.rate.toFixed(2)+" > 2 * insulinReq. Setting temp basal of "+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t)):void 0===t.duration||0===t.duration?(Re.reason+="no temp, setting "+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t)):t.duration>5&&r(Da,i)<=r(t.rate,i)?(Re.reason+="temp "+t.rate+" >~ req "+Da+"U/hr. ",Re):(Re.reason+="temp "+t.rate+"<"+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t))}},6880:(e,t,a)=>{var r=a(6654);e.exports=function(e,t){var a=20;void 0!==t&&"string"==typeof t.model&&(r(t.model,"54")||r(t.model,"23"))&&(a=40);return e<1?Math.round(e*a)/a:e<10?Math.round(20*e)/20:Math.round(10*e)/10}},2705:(e,t,a)=>{var r=a(5639).Symbol;e.exports=r},9932:e=>{e.exports=function(e,t){for(var a=-1,r=null==e?0:e.length,o=Array(r);++a{e.exports=function(e,t,a){return e==e&&(void 0!==a&&(e=e<=a?e:a),void 0!==t&&(e=e>=t?e:t)),e}},4239:(e,t,a)=>{var r=a(2705),o=a(9607),n=a(2333),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?o(e):n(e)}},531:(e,t,a)=>{var r=a(2705),o=a(9932),n=a(1469),i=a(3448),s=r?r.prototype:void 0,l=s?s.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(n(t))return o(t,e)+"";if(i(t))return l?l.call(t):"";var a=t+"";return"0"==a&&1/t==-Infinity?"-0":a}},7561:(e,t,a)=>{var r=a(7990),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},1957:(e,t,a)=>{var r="object"==typeof a.g&&a.g&&a.g.Object===Object&&a.g;e.exports=r},9607:(e,t,a)=>{var r=a(2705),o=Object.prototype,n=o.hasOwnProperty,i=o.toString,s=r?r.toStringTag:void 0;e.exports=function(e){var t=n.call(e,s),a=e[s];try{e[s]=void 0;var r=!0}catch(e){}var o=i.call(e);return r&&(t?e[s]=a:delete e[s]),o}},2333:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},5639:(e,t,a)=>{var r=a(1957),o="object"==typeof self&&self&&self.Object===Object&&self,n=r||o||Function("return this")();e.exports=n},7990:e=>{var t=/\s/;e.exports=function(e){for(var a=e.length;a--&&t.test(e.charAt(a)););return a}},6654:(e,t,a)=>{var r=a(9750),o=a(531),n=a(554),i=a(9833);e.exports=function(e,t,a){e=i(e),t=o(t);var s=e.length,l=a=void 0===a?s:r(n(a),0,s);return(a-=t.length)>=0&&e.slice(a,l)==t}},1469:e=>{var t=Array.isArray;e.exports=t},3218:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},7005:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},3448:(e,t,a)=>{var r=a(4239),o=a(7005);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},8601:(e,t,a)=>{var r=a(4841),o=1/0;e.exports=function(e){return e?(e=r(e))===o||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}},554:(e,t,a)=>{var r=a(8601);e.exports=function(e){var t=r(e),a=t%1;return t==t?a?t-a:t:0}},4841:(e,t,a)=>{var r=a(7561),o=a(3218),n=a(3448),i=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,l=/^0o[0-7]+$/i,m=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(n(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var a=s.test(e);return a||l.test(e)?m(e.slice(2),a?2:8):i.test(e)?NaN:+e}},9833:(e,t,a)=>{var r=a(531);e.exports=function(e){return null==e?"":r(e)}}},t={};function a(r){var o=t[r];if(void 0!==o)return o.exports;var n=t[r]={exports:{}};return e[r](n,n.exports,a),n.exports}a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}();var r=a(5546);freeaps_determineBasal=r})();
\ No newline at end of file
+var freeaps_determineBasal;(()=>{var e={5546:(e,t,a)=>{var r=a(6880);function o(e,t){t||(t=0);var a=Math.pow(10,t);return Math.round(e*a)/a}function n(e,t){return"mmol/L"===t.out_units?o(.0555*e,1):Math.round(e)}e.exports=function(e,t,a,i,s,l,m,u,d,c,g,h,p,f){var v=i.min_bg,B=0,b="",M="",_="",y="",x="",D=0,w=0,S=0,T=0,C=0,U=0;const G=f.weightedAverage;var O=1,R=i.sens,A=i.carb_ratio;f.useOverride&&(R/=O=f.overridePercentage/100,A/=O);const I=i.weightPercentage,j=f.average_total_data;function F(e,t){var a=e.getTime();return new Date(a+36e5*t)}function P(e){var t=i.bolus_increment;.025!=t&&(t=.05);var a=e/t;return a>=1?o(Math.floor(a)*t,5):0}function E(e){function t(e){return e<10&&(e="0"+e),e}return t(e.getHours())+":"+t(e.getMinutes())+":00"}function q(e,t){var a=new Date("1/1/1999 "+e),r=new Date("1/1/1999 "+t);return(a.getTime()-r.getTime())/36e5}function W(e,t){var a=0,r=t,o=(e-t)/36e5,n=0,i=o,s=0;do{if(o>0){var l=E(r),m=p[0].rate;for(let e=0;e=(s=q(p[e+1].start,p[e].start))?n=s:o=(s=24-q(p[e].start,t))?n=s:ou)if(e+1=(s=q(d,l))?n=s:o=(s=q("23:59:59",l))?n=s:o0&&o21)C=W(k,(z=24-B,N=k.getTime(),new Date(N-36e5*z))),y="24 hours of data is required for an accurate tdd calculation. Currently only "+B.toPrecision(3)+" hours of pump history data are available. Using your pump scheduled basals to fill in the missing hours. Scheduled basals added: "+C.toPrecision(5)+" U. ";else B<21?(ae=!1,enableDynamicCR=!1):y=""}else console.log("Pumphistory is empty!"),ae=!1,enableDynamicCR=!1;var z,N;for(let e=0;e0){D=e,U=g[e].rate;var H=g[e-1]["duration (min)"]/60,Z=H,$=new Date(g[e-1].timestamp),J=$,K=0;do{if(e--,0==e){J=new Date;break}if("TempBasal"==g[e]._type||"PumpSuspend"==g[e]._type){J=new Date(g[e].timestamp);break}var Q=e-2;if(Q>=0&&"Rewind"==g[Q]._type){let e=g[Q].timestamp;for(;Q-1>=0&&"Prime"==g[Q-=1]._type;)K=(g[Q].timestamp-e)/36e5;K>=H&&(J=e,K=0)}}while(e>0);var V=(J-$)/36e5;V0&&(--r,"TempBasal"==g[r]._type)){a=new Date(g[r].timestamp);break}}while(r>0);(a-t)/36e5>0&&(C+=W(a,t))}for(let e=g.length-1;e>0;e--)if("TempBasalDuration"==g[e]._type){let t=g[e]["duration (min)"]/60,a=new Date(g[e].timestamp);var X=a;let r=e;do{if(--r,r>=0&&("TempBasal"==g[r]._type||"PumpSuspend"==g[r]._type)){X=new Date(g[r].timestamp);break}}while(r>0);if(0==e&&"TempBasalDuration"==g[0]._type&&(X=new Date,t=g[e]["duration (min)"]/60),(X-a)/36e5-t>0){C+=W(X,F(a,t))}}var Y,ee={TDD:o(w=T+S+C,5),bolus:o(T,5),temp_basal:o(S,5),scheduled_basal:o(C,5)};B>21?(M=". Bolus insulin: "+T.toPrecision(5)+" U",_=". Temporary basal insulin: "+S.toPrecision(5)+" U",b=". Insulin with scheduled basal rate: "+C.toPrecision(5)+" U",x=y+(" TDD past 24h is: "+w.toPrecision(5)+" U")+M+_+b,tddReason=", Total insulin: "+o(w,2)+" U, "+o(T/w*100,0)+"% Bolus "+o((S+C)/w*100,0)+"% Basal"):tddReason=", TDD: Not enough pumpData (< 21h)";const te=e.glucose;var ae=h.useNewFormula;const re=h.enableDynamicCR,oe=Math.min(i.autosens_min,i.autosens_max),ne=Math.max(i.autosens_min,i.autosens_max),ie=h.adjustmentFactor,se=i.min_bg;var le=!1,me="",ue=1,de="";j>0&&(ue=G/j),de=ue>1?"Basal adjustment with a 24 hour to total average (up to 14 days of data) TDD ratio (limited by Autosens max setting). Basal Ratio: "+(ue=o(ue=Math.min(ue,i.autosens_max),2))+". Upper limit = Autosens max ("+i.autosens_max+")":ue<1?"Basal adjustment with a 24 hour to to total average (up to 14 days of data) TDD ratio (limited by Autosens min setting). Basal Ratio: "+(ue=o(ue=Math.max(ue,i.autosens_min),2))+". Lower limit = Autosens min ("+i.autosens_min+")":"Basal adjusted with a 24 hour to total average (up to 14 days of data) TDD ratio: "+ue,de=", Basal ratio: "+ue,(i.high_temptarget_raises_sensitivity||i.exercise_mode||f.isEnabled)&&(le=!0),se>=118&&le&&(ae=!1,me="Dynamic ISF temporarily off due to a high temp target/exercising. Current min target: "+se);var ce=", Dynamic ratios log: ",ge=", AF: "+ie,he="BG: "+te+" mg/dl ("+(.0555*te).toPrecision(2)+" mmol/l)",pe="",fe="";const ve=h.curve,Be=h.insulinPeakTime,be=h.useCustomPeakTime;var Me=55,_e=65;switch(ve){case"rapid-acting":_e=65;break;case"ultra-rapid":_e=50}be?(Me=120-Be,console.log("Custom insulinpeakTime set to :"+Be+", insulinFactor: "+Me)):(Me=120-_e,console.log("insulinFactor set to : "+Me)),Y=w,I<1&&G>0&&(w=G,console.log("Using weighted TDD average: "+o(w,2)+" U, instead of past 24 h ("+o(Y,2)+" U), weight: "+I),fe=", Weighted TDD: "+o(w,2)+" U");const ye=h.sigmoid;var xe="";if(ae){var De=R*ie*w*Math.log(te/Me+1)/1800;pe=", Logarithmic formula"}if(ae&&ye){const e=oe,t=ne-e,a=.0555*(te-i.min_bg);var we=ue;const r=ne-1,o=Math.log10(1/r-e/r)/Math.log10(Math.E),n=a*ie*we+o;De=t/(1+Math.exp(-n))+e,pe=", Sigmoid function"}var Se=A;const Te=o(A,1);var Ce="",Ue="";if(ae&&w>0){if(Ce=", Dynamic ISF/CR: On/",De>ne?(me=", Dynamic ISF limited by autosens_max setting: "+ne+" ("+o(De,2)+"), ",Ue=", Autosens/Dynamic Limit: "+ne+" ("+o(De,2)+")",De=ne):De1&&(Ge=(De-1)/2+1);var Oe=" CR: "+(Se=o(Se/Ge,2))+" g/U";A=Se}else Oe=" CR: "+Se+" g/U",Ce+="Off";const e=R/De;xe=". Using Sigmoid function, the autosens ratio has been adjusted with sigmoid factor to: "+o(s.ratio,2)+". New ISF = "+o(e,2)+" mg/dl ("+o(.0555*e,2)+" (mmol/l). CR adjusted from "+o(Te,2)+" to "+o(Se,2)+" ("+o(.0555*A,2)+" mmol/l).",me+=ye?xe:", Dynamic autosens.ratio set to "+o(De,2)+" with ISF: "+e.toPrecision(3)+" mg/dl/U ("+(.0555*e).toPrecision(3)+" mmol/l/U)",s.ratio=De,x+=ce+he+ge+pe+me+Ce+Oe+fe}else x+=ce+"Dynamic Settings disabled";console.log(x),ae||re?ae&&i.tddAdjBasal?tddReason+=Ce+pe+Ue+ge+de:ae&&!i.tddAdjBasal&&(tddReason+=Ce+pe+Ue+ge):tddReason+="";var Re={},Ae=new Date;if(c&&(Ae=c),void 0===i||void 0===i.current_basal)return Re.error="Error: could not get current basal rate",Re;var Ie=r(i.current_basal,i)*O,je=Ie;f.useOverride&&(0==f.duration?console.log("Profile Override is active. Override "+o(100*O,0)+"%. Override Duration: Enabled indefinitely"):console.log("Profile Override is active. Override "+o(100*O,0)+"%. Override Expires in: "+f.duration+" min."));var Fe=new Date;c&&(Fe=c);var Pe,Ee=new Date(e.date),qe=o((Fe-Ee)/60/1e3,1),We=e.glucose,ke=e.noise;Pe=e.delta>-.5?"+"+o(e.delta,0):o(e.delta,0);var Le=Math.min(e.delta,e.short_avgdelta),ze=Math.min(e.short_avgdelta,e.long_avgdelta),Ne=Math.max(e.delta,e.short_avgdelta,e.long_avgdelta);(We<=10||38===We||ke>=3)&&(Re.reason="CGM is calibrating, in ??? state, or noise is high");if(We>60&&0==e.delta&&e.short_avgdelta>-1&&e.short_avgdelta<1&&e.long_avgdelta>-1&&e.long_avgdelta<1&&("fakecgm"==e.device?(console.error("CGM data is unchanged ("+n(We,i)+"+"+n(e.delta,i)+") for 5m w/ "+n(e.short_avgdelta,i)+" mg/dL ~15m change & "+n(e.long_avgdelta,2)+" mg/dL ~45m change"),console.error("Simulator mode detected ("+e.device+"): continuing anyway")):!0),qe>12||qe<-5?Re.reason="If current system time "+Fe+" is correct, then BG data is too old. The last BG data was read "+qe+"m ago at "+Ee:0===e.short_avgdelta&&0===e.long_avgdelta&&(e.last_cal&&e.last_cal<3?Re.reason="CGM was just calibrated":Re.reason="CGM data is unchanged ("+n(We,i)+"+"+n(e.delta,i)+") for 5m w/ "+n(e.short_avgdelta,i)+" mg/dL ~15m change & "+n(e.long_avgdelta,i)+" mg/dL ~45m change"),We<=10||38===We||ke>=3||qe>12||qe<-5||0===e.short_avgdelta&&0===e.long_avgdelta)return t.rate>=je?(Re.reason+=". Canceling high temp basal of "+t.rate,Re.deliverAt=Ae,Re.temp="absolute",Re.duration=0,Re.rate=0,Re):0===t.rate&&t.duration>30?(Re.reason+=". Shortening "+t.duration+"m long zero temp to 30m. ",Re.deliverAt=Ae,Re.temp="absolute",Re.duration=30,Re.rate=0,Re):(Re.reason+=". Temp "+t.rate+" <= current basal "+je+"U/hr; doing nothing. ",Re);var He,Ze,$e,Je,Ke=i.max_iob;if(void 0!==i.min_bg&&(Ze=i.min_bg),void 0!==i.max_bg&&($e=i.max_bg),void 0!==i.enableSMB_high_bg_target&&(Je=i.enableSMB_high_bg_target),void 0===i.min_bg||void 0===i.max_bg)return Re.error="Error: could not determine target_bg. ",Re;He=(i.min_bg+i.max_bg)/2;var Qe=i.exercise_mode||i.high_temptarget_raises_sensitivity||f.isEnabled,Ve=100,Xe=160;if(Xe=i.half_basal_exercise_target,f.isEnabled){const e=f.hbt;console.log("Half Basal Target used: "+n(e,i)+" "+i.out_units),Xe=e}else console.log("Default Half Basal Target used: "+n(Xe,i)+" "+i.out_units);if(Qe&&i.temptargetSet&&He>Ve||i.low_temptarget_lowers_sensitivity&&i.temptargetSet&&He=He&&sensitivityRatio0&&(process.stderr.write("TDD-adjustment of basals activated, using tdd24h_14d_Ratio "+o(ue,2)+", TDD 24h = "+o(Y,2)+"U, Weighted average TDD = "+o(G,2)+"U, (Weight percentage = "+I+"), Total data of TDDs (up to 14 days) average = "+o(j,2)+"U. "),je!==Ie*O?process.stderr.write("Adjusting basal from "+Ie*O+" U/h to "+je+" U/h; "):process.stderr.write("Basal unchanged: "+je+" U/h; "))),i.temptargetSet);else if(void 0!==s&&s&&(i.sensitivity_raises_target&&s.ratio<1||i.resistance_lowers_target&&s.ratio>1)){Ze=o((Ze-60)/s.ratio)+60,$e=o(($e-60)/s.ratio)+60;var et=o((He-60)/s.ratio)+60;He===(et=Math.max(80,et))?process.stderr.write("target_bg unchanged: "+et+"; "):process.stderr.write("target_bg from "+He+" to "+et+"; "),He=et}var tt=200,at=200,rt=200;if(e.noise>=2){var ot=Math.max(1.1,i.noisyCGMTargetMultiplier);Math.min(250,i.maxRaw);tt=o(Math.min(200,Ze*ot)),at=o(Math.min(200,He*ot)),rt=o(Math.min(200,$e*ot)),process.stderr.write("Raising target_bg for noisy / raw CGM data, from "+He+" to "+at+"; "),Ze=tt,He=at,$e=rt}var nt=Ze-.5*(Ze-40),it=i.threshold_setting;it>nt&&it<=120&&it>=65?(console.error("Threshold changed in settings from "+n(nt,i)+" to "+n(it,i)+". "),nt=it):console.error("Current threshold: "+n(nt,i));var st="",lt=(o(R,1),R);if(void 0!==s&&s&&((lt=o(lt=R/sensitivityRatio,1))!==R?process.stderr.write("ISF from "+n(R,i)+" to "+n(lt,i)):process.stderr.write("ISF unchanged: "+n(lt,i)),st+="Autosens ratio: "+o(sensitivityRatio,2)+", ISF: "+n(R,i)+"→"+n(lt,i)),console.error("CR:"+A),void 0===a)return Re.error="Error: iob_data undefined. ",Re;var mt,ut=a;if(a.length,a.length>1&&(a=ut[0]),void 0===a.activity||void 0===a.iob)return Re.error="Error: iob_data missing some property. ",Re;var dt=((mt=void 0!==a.lastTemp?o((new Date(Fe).getTime()-a.lastTemp.date)/6e4):0)+t.duration)%30;if(console.error("currenttemp:"+t.rate+" lastTempAge:"+mt+"m, tempModulus:"+dt+"m"),Re.temp="absolute",Re.deliverAt=Ae,u&&t&&a.lastTemp&&t.rate!==a.lastTemp.rate&&mt>10&&t.duration)return Re.reason="Warning: currenttemp rate "+t.rate+" != lastTemp rate "+a.lastTemp.rate+" from pumphistory; canceling temp",m.setTempBasal(0,0,i,Re,t);if(t&&a.lastTemp&&t.duration>0){var ct=mt-a.lastTemp.duration;if(ct>5&&mt>10)return Re.reason="Warning: currenttemp running but lastTemp from pumphistory ended "+ct+"m ago; canceling temp",m.setTempBasal(0,0,i,Re,t)}var gt=o(-a.activity*lt*5,2),ht=o(6*(Le-gt));ht<0&&(ht=o(6*(ze-gt)))<0&&(ht=o(6*(e.long_avgdelta-gt)));var pt=We,ft=(pt=a.iob>0?o(We-a.iob*lt):o(We-a.iob*Math.min(lt,R)))+ht;if(void 0===ft||isNaN(ft))return Re.error="Error: could not calculate eventualBG. Sensitivity: "+lt+" Deviation: "+ht,Re;var vt=function(e,t,a){return o(a+(e-t)/24,1)}(He,ft,gt);Re={temp:"absolute",bg:We,tick:Pe,eventualBG:ft,insulinReq:0,reservoir:d,deliverAt:Ae,sensitivityRatio,TDD:Y,insulin:ee,current_target:He};var Bt=[],bt=[],Mt=[],_t=[];Bt.push(We),bt.push(We),_t.push(We),Mt.push(We);var yt=function(e,t,a,r,o,i){return t?!e.allowSMB_with_high_temptarget&&e.temptargetSet&&o>100?(console.error("SMB disabled due to high temptarget of "+o),!1):!0===a.bwFound&&!1===e.A52_risk_enable?(console.error("SMB disabled due to Bolus Wizard activity in the last 6 hours."),!1):!0===e.enableSMB_always?(a.bwFound?console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled due to enableSMB_always"),!0):!0===e.enableSMB_with_COB&&a.mealCOB?(a.bwCarbs?console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for COB of "+a.mealCOB),!0):!0===e.enableSMB_after_carbs&&a.carbs?(a.bwCarbs?console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for 6h after carb entry"),!0):!0===e.enableSMB_with_temptarget&&e.temptargetSet&&o<100?(a.bwFound?console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("SMB enabled for temptarget of "+n(o,e)),!0):!0===e.enableSMB_high_bg&&null!==i&&r>=i?(console.error("Checking BG to see if High for SMB enablement."),console.error("Current BG",r," | High BG ",i),a.bwFound?console.error("Warning: High BG SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"):console.error("High BG detected. Enabling SMB."),!0):(console.error("SMB disabled (no enableSMB preferences active or no condition satisfied)"),!1):(console.error("SMB disabled (!microBolusAllowed)"),!1)}(i,u,l,We,He,Je),xt=i.enableUAM,Dt=0,wt=0;Dt=o(Le-gt,1);var St=o(Le-gt,1);csf=lt/A,console.error("profile.sens:"+n(R,i)+", sens:"+n(lt,i)+", CSF:"+o(csf,1));var Tt=o(30*csf*5/60,1);Dt>Tt&&(console.error("Limiting carb impact from "+Dt+" to "+Tt+"mg/dL/5m (30g/h)"),Dt=Tt);var Ct=3;sensitivityRatio&&(Ct/=sensitivityRatio);var Ut=Ct;if(l.carbs){Ct=Math.max(Ct,l.mealCOB/20);var Gt=o((new Date(Fe).getTime()-l.lastCarbTime)/6e4),Ot=(l.carbs-l.mealCOB)/l.carbs;Ut=o(Ut=Ct+1.5*Gt/60,1),console.error("Last carbs "+Gt+" minutes ago; remainingCATime:"+Ut+"hours; "+o(100*Ot,1)+"% carbs absorbed")}var Rt=Math.max(0,Dt/5*60*Ut/2)/csf,At=90,It=1;i.remainingCarbsCap&&(At=Math.min(90,i.remainingCarbsCap)),i.remainingCarbsFraction&&(It=Math.min(1,i.remainingCarbsFraction));var jt=1-It,Ft=Math.max(0,l.mealCOB-Rt-l.carbs*jt),Pt=(Ft=Math.min(At,Ft))*csf*5/60/(Ut/2),Et=o(l.slopeFromMaxDeviation,2),qt=o(l.slopeFromMinDeviation,2),Wt=Math.min(Et,-qt/3);wt=0===Dt?0:Math.min(60*Ut/5/2,Math.max(0,l.mealCOB*csf/Dt)),console.error("Carb Impact:"+Dt+"mg/dL per 5m; CI Duration:"+o(5*wt/60*2,1)+"hours; remaining CI ("+Ut/2+"h peak):"+o(Pt,1)+"mg/dL per 5m");var kt,Lt,zt,Nt,Ht,Zt=999,$t=999,Jt=999,Kt=We,Qt=999,Vt=999,Xt=999,Yt=999,ea=ft,ta=We,aa=We,ra=0,oa=[],na=[];try{ut.forEach((function(e){var t=o(-e.activity*lt*5,2),a=o(-e.iobWithZeroTemp.activity*lt*5,2),r=pt,n=Dt*(1-Math.min(1,bt.length/12));if(!0===(ae&&!ye))ea=bt[bt.length-1]+o(-e.activity*(1800/(w*ie*Math.log(Math.max(bt[bt.length-1],39)/Me+1)))*5,2)+n,r=_t[_t.length-1]+o(-e.iobWithZeroTemp.activity*(1800/(w*ie*Math.log(Math.max(_t[_t.length-1],39)/Me+1)))*5,2),console.log("Dynamic ISF (Logarithmic Formula) )adjusted predictions for IOB and ZT: IOBpredBG: "+o(ea,2)+" , ZTpredBG: "+o(r,2));else ea=bt[bt.length-1]+t+n,r=_t[_t.length-1]+a;var i=Math.max(0,Math.max(0,Dt)*(1-Bt.length/Math.max(2*wt,1))),s=Math.min(Bt.length,12*Ut-Bt.length),l=Math.max(0,s/(Ut/2*12)*Pt);i+l,oa.push(o(l,0)),na.push(o(i,0)),COBpredBG=Bt[Bt.length-1]+t+Math.min(0,n)+i+l;var m=Math.max(0,St+Mt.length*Wt),u=Math.max(0,St*(1-Mt.length/Math.max(36,1))),d=Math.min(m,u);if(d>0&&(ra=o(5*(Mt.length+1)/60,1)),!0===(ae&&!ye))UAMpredBG=Mt[Mt.length-1]+o(-e.activity*(1800/(w*ie*Math.log(Math.max(Mt[Mt.length-1],39)/Me+1)))*5,2)+Math.min(0,n)+d,console.log("Dynamic ISF (Logarithmic Formula) adjusted prediction for UAM: UAMpredBG: "+o(UAMpredBG,2));else UAMpredBG=Mt[Mt.length-1]+t+Math.min(0,n)+d;bt.length<48&&bt.push(ea),Bt.length<48&&Bt.push(COBpredBG),Mt.length<48&&Mt.push(UAMpredBG),_t.length<48&&_t.push(r),COBpredBG18&&eata&&(ta=ea),(wt||Pt>0)&&Bt.length>18&&COBpredBG<$t&&($t=o(COBpredBG)),(wt||Pt>0)&&COBpredBG>ta&&(aa=COBpredBG),xt&&Mt.length>12&&UAMpredBGta&&UAMpredBG}))}catch(e){console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled")}l.mealCOB&&(console.error("predCIs (mg/dL/5m):"+na.join(" ")),console.error("remainingCIs: "+oa.join(" "))),Re.predBGs={},bt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))}));for(var ia=bt.length-1;ia>12&&bt[ia-1]===bt[ia];ia--)bt.pop();for(Re.predBGs.IOB=bt,zt=o(bt[bt.length-1]),_t.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=_t.length-1;ia>6&&!(_t[ia-1]>=_t[ia]||_t[ia]<=He);ia--)_t.pop();if(Re.predBGs.ZT=_t,o(_t[_t.length-1]),l.mealCOB>0&&(Dt>0||Pt>0)){for(Bt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=Bt.length-1;ia>12&&Bt[ia-1]===Bt[ia];ia--)Bt.pop();Re.predBGs.COB=Bt,Nt=o(Bt[Bt.length-1]),ft=Math.max(ft,o(Bt[Bt.length-1]))}if(Dt>0||Pt>0){if(xt){for(Mt.forEach((function(e,t,a){a[t]=o(Math.min(401,Math.max(39,e)))})),ia=Mt.length-1;ia>12&&Mt[ia-1]===Mt[ia];ia--)Mt.pop();Re.predBGs.UAM=Mt,Ht=o(Mt[Mt.length-1]),Mt[Mt.length-1]&&(ft=Math.max(ft,o(Mt[Mt.length-1])))}Re.eventualBG=ft}console.error("UAM Impact:"+St+"mg/dL per 5m; UAM Duration:"+ra+"hours"),Zt=Math.max(39,Zt),$t=Math.max(39,$t),Jt=Math.max(39,Jt),kt=o(Zt);var sa=l.mealCOB/l.carbs;Lt=o(Jt<999&&$t<999?(1-sa)*UAMpredBG+sa*COBpredBG:$t<999?(ea+COBpredBG)/2:Jt<999?(ea+UAMpredBG)/2:ea),Yt>Lt&&(Lt=Yt),Kt=o(Kt=wt||Pt>0?xt?sa*Qt+(1-sa)*Vt:Qt:xt?Vt:Xt);var la=Jt;if(YtJt&&(la=(Jt+Yt)/2);if(la=o(la),l.carbs)if(!xt&&$t<999)kt=o(Math.max(Zt,$t));else if($t<999){var ua=sa*$t+(1-sa)*la;kt=o(Math.max(Zt,$t,ua))}else kt=xt?la:Kt;else xt&&(kt=o(Math.max(Zt,la)));kt=Math.min(kt,Lt),process.stderr.write("minPredBG: "+kt+" minIOBPredBG: "+Zt+" minZTGuardBG: "+Yt),$t<999&&process.stderr.write(" minCOBPredBG: "+$t),Jt<999&&process.stderr.write(" minUAMPredBG: "+Jt),console.error(" avgPredBG:"+Lt+" COB/Carbs:"+l.mealCOB+"/"+l.carbs),aa>We&&(kt=Math.min(kt,aa)),Re.COB=l.mealCOB,Re.IOB=a.iob,Re.BGI=n(gt,i),Re.deviation=n(ht,i),Re.ISF=n(lt,i),Re.CR=o(A,1),Re.target_bg=n(He,i),Re.TDD=o(Y,2),Re.current_target=o(He,0);var da=Re.CR;Te!=Re.CR&&(da=Te+"→"+Re.CR);var ca=Re.target_bg;He!=v&&(ca=n(v,i)+"→"+Re.target_bg),Re.reason=st+", COB: "+Re.COB+", Dev: "+Re.deviation+", BGI: "+Re.BGI+", CR: "+da+", Target: "+ca+", minPredBG "+n(kt,i)+", minGuardBG "+n(Kt,i)+", IOBpredBG "+n(zt,i),Nt>0&&(Re.reason+=", COBpredBG "+n(Nt,i)),Ht>0&&(Re.reason+=", UAMpredBG "+n(Ht,i)),Re.reason+=tddReason,Re.reason+="; ";var ga=pt;ga<40&&(ga=Math.min(Kt,ga));var ha,pa=nt-ga,fa=240,va=240;if(l.mealCOB>0&&(Dt>0||Pt>0)){for(ia=0;iaha*We&&(console.error("maxDelta "+n(Ne,i)+" > "+100*ha+"% of BG "+n(We,i)+" - disabling SMB"),Re.reason+="maxDelta "+n(Ne,i)+" > "+100*ha+"% of BG "+n(We,i)+" - SMB disabled!, ",yt=!1),console.error("BG projected to remain above "+n(Ze,i)+" for "+fa+"minutes"),(va<240||fa<60)&&console.error("BG projected to remain above "+n(nt,i)+" for "+va+"minutes");var Ba=va,ba=i.current_basal*O*lt*Ba/60,Ma=Math.max(0,l.mealCOB-.25*l.carbs),_a=(pa-ba)/csf-Ma;ba=o(ba),_a=o(_a),console.error("naive_eventualBG:",pt,"bgUndershoot:",pa,"zeroTempDuration:",Ba,"zeroTempEffect:",ba,"carbsReq:",_a),"Could not parse clock data"==l.reason?console.error("carbsReq unknown: Could not parse clock data"):_a>=i.carbsReqThreshold&&va<=45&&(Re.carbsReq=_a,Re.reason+=_a+" add'l carbs req w/in "+va+"m; ");var ya=0;if(We0&&Le>vt)Re.reason+="IOB "+a.iob+" < "+o(-i.current_basal*O*20/60,2),Re.reason+=" and minDelta "+n(Le,i)+" > expectedDelta "+n(vt,i)+"; ";else if(We=55)return Re.reason+="; Canceling temp at "+Re.deliverAt.getMinutes()+"m past the hour. ",m.setTempBasal(0,0,i,Re,t);var xa=0,Da=je;if(ftvt&&Le>0&&!_a)return pt<40?(Re.reason+=", naive_eventualBG < 40. ",m.setTempBasal(0,30,i,Re,t)):(e.delta>Le?Re.reason+=", but Delta "+n(Pe,i)+" > expectedDelta "+n(vt,i):Re.reason+=", but Min. Delta "+Le.toFixed(2)+" > Exp. Delta "+n(vt,i),t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t)));xa=o(xa=2*Math.min(0,(ft-He)/lt),2);var wa=Math.min(0,(pt-He)/lt);if(wa=o(wa,2),Le<0&&Le>vt)xa=o(xa*(Le/vt),2);if(Da=r(Da=je+2*xa,i),t.duration*(t.rate-je)/605&&Da>=.8*t.rate)return Re.reason+=", temp "+t.rate+" ~< req "+Da+"U/hr. ",Re;if(Da<=0){if((ya=o(60*((pa=He-pt)/lt)/i.current_basal*O))<0?ya=0:(ya=30*o(ya/30),ya=Math.min(120,Math.max(0,ya))),ya>0)return Re.reason+=", setting "+ya+"m zero temp. ",m.setTempBasal(Da,ya,i,Re,t)}else Re.reason+=", setting "+Da+"U/hr. ";return m.setTempBasal(Da,30,i,Re,t)}if(Le "+n(Ze,i)+" but Delta "+n(Pe,i)+" < Exp. Delta "+n(vt,i):Re.reason+="Eventual BG "+n(ft,i)+" > "+n(Ze,i)+" but Min. Delta "+Le.toFixed(2)+" < Exp. Delta "+n(vt,i),t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));if(Math.min(ft,kt)<$e&&(!u||!yt))return Re.reason+=n(ft,i)+"-"+n(kt,i)+" in range: no temp required",t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));if(ft>=$e&&(Re.reason+="Eventual BG "+n(ft,i)+" >= "+n($e,i)+", "),a.iob>Ke)return Re.reason+="IOB "+o(a.iob,2)+" > max_iob "+Ke,t.duration>15&&r(je,i)===r(t.rate,i)?(Re.reason+=", temp "+t.rate+" ~ req "+je+"U/hr. ",Re):(Re.reason+="; setting current basal of "+je+" as temp. ",m.setTempBasal(je,30,i,Re,t));(xa=o((Math.min(kt,ft)-He)/lt,2))>Ke-a.iob?(console.error("SMB limited by maxIOB: "+Ke-a.iob+" (. insulinReq: "+xa+" U)"),Re.reason+="max_iob "+Ke+", ",xa=Ke-a.iob):console.error("SMB not limited by maxIOB ( insulinReq: "+xa+" U)."),Da=r(Da=je+2*xa,i),xa=o(xa,3),Re.insulinReq=xa;var Sa=o((new Date(Fe).getTime()-a.lastBolusTime)/6e4,1);if(u&&yt&&We>nt){var Ta=o(l.mealCOB/A,3),Ca=0;void 0===i.maxSMBBasalMinutes?(Ca=o(i.current_basal*O*30/60,1),console.error("profile.maxSMBBasalMinutes undefined: defaulting to 30m"),xa>Ca&&console.error("SMB limited by maxBolus: "+Ca+" ( "+xa+" U)")):a.iob>Ta&&a.iob>0?(console.error("IOB"+a.iob+"> COB"+l.mealCOB+"; mealInsulinReq ="+Ta),i.maxUAMSMBBasalMinutes?(console.error("profile.maxUAMSMBBasalMinutes: "+i.maxUAMSMBBasalMinutes+", profile.current_basal: "+i.current_basal*O),Ca=o(i.current_basal*O*i.maxUAMSMBBasalMinutes/60,1)):(console.error("profile.maxUAMSMBBasalMinutes undefined: defaulting to 30m"),Ca=o(i.current_basal*O*30/60,1)),xa>Ca?console.error("SMB limited by maxUAMSMBBasalMinutes [ "+i.maxUAMSMBBasalMinutes+"m ]: "+Ca+"U ( "+xa+"U )"):console.error("SMB is not limited by maxUAMSMBBasalMinutes. ( insulinReq: "+xa+"U )")):(console.error("profile.maxSMBBasalMinutes: "+i.maxSMBBasalMinutes+", profile.current_basal: "+i.current_basal*O),xa>(Ca=o(i.current_basal*i.maxSMBBasalMinutes/60,1))?console.error("SMB limited by maxSMBBasalMinutes: "+i.maxSMBBasalMinutes+"m ]: "+Ca+"U ( insulinReq: "+xa+"U )"):console.error("SMB is not limited by maxSMBBasalMinutes. ( insulinReq: "+xa+"U )"));var Ua=i.bolus_increment,Ga=1/Ua,Oa=i.smb_delivery_ratio;Oa>.5&&console.error("SMB Delivery Ratio increased from default 0.5 to "+o(Oa,2));var Ra=Math.min(xa*Oa,Ca);Ra=Math.floor(Ra*Ga)/Ga,ya=o(60*((He-(pt+Zt)/2)/lt)/i.current_basal*O),xa>0&&Ra=30?(ya=30*o(ya/30),ya=Math.min(60,Math.max(0,ya))):(Aa=o(je*ya/30,2),ya=30),Re.reason+=" insulinReq "+xa,Ra>=Ca&&(Re.reason+="; maxBolus "+Ca),ya>0&&(Re.reason+="; setting "+ya+"m low temp of "+Aa+"U/h"),Re.reason+=". ";var Ia=3;i.SMBInterval&&(Ia=Math.min(10,Math.max(1,i.SMBInterval)));var ja=o(Ia-Sa,0),Fa=o(60*(Ia-Sa),0)%60;if(console.error("naive_eventualBG "+pt+","+ya+"m "+Aa+"U/h temp needed; last bolus "+Sa+"m ago; maxBolus: "+Ca),Sa>Ia?Ra>0&&(Re.units=Ra,Re.reason+="Microbolusing "+Ra+"U. "):Re.reason+="Waiting "+ja+"m "+Fa+"s to microbolus again. ",ya>0)return Re.rate=Aa,Re.duration=ya,Re}var Pa=m.getMaxSafeBasal(i);return Da>Pa&&(Re.reason+="adj. req. rate: "+Da+" to maxSafeBasal: "+o(Pa,2)+", ",Da=r(Pa,i)),t.duration*(t.rate-je)/60>=2*xa?(Re.reason+=t.duration+"m@"+t.rate.toFixed(2)+" > 2 * insulinReq. Setting temp basal of "+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t)):void 0===t.duration||0===t.duration?(Re.reason+="no temp, setting "+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t)):t.duration>5&&r(Da,i)<=r(t.rate,i)?(Re.reason+="temp "+t.rate+" >~ req "+Da+"U/hr. ",Re):(Re.reason+="temp "+t.rate+"<"+Da+"U/hr. ",m.setTempBasal(Da,30,i,Re,t))}},6880:(e,t,a)=>{var r=a(6654);e.exports=function(e,t){var a=20;void 0!==t&&"string"==typeof t.model&&(r(t.model,"54")||r(t.model,"23"))&&(a=40);return e<1?Math.round(e*a)/a:e<10?Math.round(20*e)/20:Math.round(10*e)/10}},2705:(e,t,a)=>{var r=a(5639).Symbol;e.exports=r},9932:e=>{e.exports=function(e,t){for(var a=-1,r=null==e?0:e.length,o=Array(r);++a{e.exports=function(e,t,a){return e==e&&(void 0!==a&&(e=e<=a?e:a),void 0!==t&&(e=e>=t?e:t)),e}},4239:(e,t,a)=>{var r=a(2705),o=a(9607),n=a(2333),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?o(e):n(e)}},531:(e,t,a)=>{var r=a(2705),o=a(9932),n=a(1469),i=a(3448),s=r?r.prototype:void 0,l=s?s.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(n(t))return o(t,e)+"";if(i(t))return l?l.call(t):"";var a=t+"";return"0"==a&&1/t==-Infinity?"-0":a}},7561:(e,t,a)=>{var r=a(7990),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},1957:(e,t,a)=>{var r="object"==typeof a.g&&a.g&&a.g.Object===Object&&a.g;e.exports=r},9607:(e,t,a)=>{var r=a(2705),o=Object.prototype,n=o.hasOwnProperty,i=o.toString,s=r?r.toStringTag:void 0;e.exports=function(e){var t=n.call(e,s),a=e[s];try{e[s]=void 0;var r=!0}catch(e){}var o=i.call(e);return r&&(t?e[s]=a:delete e[s]),o}},2333:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},5639:(e,t,a)=>{var r=a(1957),o="object"==typeof self&&self&&self.Object===Object&&self,n=r||o||Function("return this")();e.exports=n},7990:e=>{var t=/\s/;e.exports=function(e){for(var a=e.length;a--&&t.test(e.charAt(a)););return a}},6654:(e,t,a)=>{var r=a(9750),o=a(531),n=a(554),i=a(9833);e.exports=function(e,t,a){e=i(e),t=o(t);var s=e.length,l=a=void 0===a?s:r(n(a),0,s);return(a-=t.length)>=0&&e.slice(a,l)==t}},1469:e=>{var t=Array.isArray;e.exports=t},3218:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},7005:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},3448:(e,t,a)=>{var r=a(4239),o=a(7005);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},8601:(e,t,a)=>{var r=a(4841),o=1/0;e.exports=function(e){return e?(e=r(e))===o||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}},554:(e,t,a)=>{var r=a(8601);e.exports=function(e){var t=r(e),a=t%1;return t==t?a?t-a:t:0}},4841:(e,t,a)=>{var r=a(7561),o=a(3218),n=a(3448),i=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,l=/^0o[0-7]+$/i,m=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(n(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var a=s.test(e);return a||l.test(e)?m(e.slice(2),a?2:8):i.test(e)?NaN:+e}},9833:(e,t,a)=>{var r=a(531);e.exports=function(e){return null==e?"":r(e)}}},t={};function a(r){var o=t[r];if(void 0!==o)return o.exports;var n=t[r]={exports:{}};return e[r](n,n.exports,a),n.exports}a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}();var r=a(5546);freeaps_determineBasal=r})();
diff --git a/FreeAPS/Sources/APS/APSManager.swift b/FreeAPS/Sources/APS/APSManager.swift
index 3e067b5643..81ee2df8e1 100644
--- a/FreeAPS/Sources/APS/APSManager.swift
+++ b/FreeAPS/Sources/APS/APSManager.swift
@@ -81,8 +81,8 @@ final class BaseAPSManager: APSManager, Injectable {
}
}
- // let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
- let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
+ let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+ // let coredataContext = CoreDataStack.shared.persistentContainer.viewContext
private var openAPS: OpenAPS!
@@ -702,232 +702,10 @@ final class BaseAPSManager: APSManager, Injectable {
}
}
nightscout.uploadStatus()
- // Update the TDD value
- tdd(enacted_: enacted)
- // Update statistics
statistics()
}
}
- private func tdd(enacted_: Suggestion) {
- let tddStartedAt = Date()
- let preferences = settingsManager.preferences
- let currentTDD = enacted_.tdd ?? 0
-
- var booleanArray = [TempTargetsSlider]()
- var overrideArray = [Override]()
- var tempTargetsArray = [TempTargets]()
- var isPercentageEnabled = false
- var useOverride = false
- var overridePercentage: Decimal = 100
- var duration: Decimal = 0
- var unlimited: Bool = false
- var newDuration: Decimal = 0
- var hbtSetting: Decimal = 160
-
- if currentTDD > 0 {
- let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
- let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
-
- var uniqEvents = [TDD]()
- var total: Decimal = 0
- var totalAmount: Decimal = 0
- var indeces: Int = 0
- var nrOfIndeces: Int = 0
-
- coredataContext.performAndWait {
- let requestTDD = TDD.fetchRequest() as NSFetchRequest
- requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
- let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
- requestTDD.sortDescriptors = [sortTDD]
- try? uniqEvents = coredataContext.fetch(requestTDD)
-
- let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest
- let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
- requestIsEnbled.sortDescriptors = [sortIsEnabled]
- // requestIsEnbled.fetchLimit = 1
- try? booleanArray = coredataContext.fetch(requestIsEnbled)
-
- let requestOverrides = Override.fetchRequest() as NSFetchRequest
- let sortOverride = NSSortDescriptor(key: "date", ascending: false)
- requestOverrides.sortDescriptors = [sortOverride]
- requestOverrides.fetchLimit = 1
- try? overrideArray = coredataContext.fetch(requestOverrides)
-
- let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest
- let sortTT = NSSortDescriptor(key: "date", ascending: false)
- requestTempTargets.sortDescriptors = [sortTT]
- requestTempTargets.fetchLimit = 1
- try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
-
- total = uniqEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
- indeces = uniqEvents.count
- // Only fetch once. Use same (previous) fetch
- let twoHoursArray = uniqEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
- nrOfIndeces = twoHoursArray.count
- totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
- }
-
- if indeces == 0 {
- indeces = 1
- }
- if nrOfIndeces == 0 {
- nrOfIndeces = 1
- }
-
- let average2hours = totalAmount / Decimal(nrOfIndeces)
- let average14 = total / Decimal(indeces)
- let weight = preferences.weightPercentage
- let weighted_average = weight * average2hours + (1 - weight) * average14
-
- isPercentageEnabled = booleanArray.first?.enabled ?? false
- useOverride = overrideArray.first?.enabled ?? false
- overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
- unlimited = overrideArray.first?.indefinite ?? true
- hbtSetting = Decimal(booleanArray.first?.hbt ?? 160)
-
- if useOverride {
- duration = (overrideArray.first?.duration ?? 0) as Decimal
- let addedMinutes = Int(duration)
- let date = overrideArray.first?.date ?? Date()
- if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
- !unlimited
- { useOverride = false }
-
- newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
- }
-
- if newDuration < 0 {
- newDuration = 0
- } else { duration = newDuration }
-
- if !useOverride {
- unlimited = true
- overridePercentage = 100
- duration = 0
- }
-
- if tempTargetsArray.first?.active ?? false || booleanArray.first?.enabled ?? false {
- var duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
- var hbt = tempTargetsArray.first?.hbt ?? 160
- if booleanArray.first?.enabled ?? false, !(booleanArray.first?.isPreset ?? false) {
- duration_ = Int(truncating: booleanArray.first?.duration ?? 0)
- hbt = booleanArray.first?.hbt ?? 160
- }
- let startDate = tempTargetsArray.first?.startDate ?? Date()
- let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
- let dd = durationPlusStart.timeIntervalSinceNow.minutes
- if dd > 0 {
- hbtSetting = Decimal(hbt)
- isPercentageEnabled = true
- } else { isPercentageEnabled = false }
- }
-
- let averages = Oref2_variables(
- average_total_data: roundDecimal(average14, 1),
- weightedAverage: roundDecimal(weighted_average, 1),
- past2hoursAverage: roundDecimal(average2hours, 1),
- date: Date(),
- isEnabled: isPercentageEnabled,
- overridePercentage: overridePercentage,
- useOverride: useOverride,
- duration: duration,
- unlimited: unlimited,
- hbt: hbtSetting
- )
- storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
-
- print("Test time of TDD: \(-1 * tddStartedAt.timeIntervalSinceNow) s")
- } else {
- coredataContext.performAndWait {
- let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest
- let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
- requestIsEnbled.sortDescriptors = [sortIsEnabled]
- try? booleanArray = coredataContext.fetch(requestIsEnbled)
-
- let requestOverrides = Override.fetchRequest() as NSFetchRequest
- let sortOverride = NSSortDescriptor(key: "date", ascending: false)
- requestOverrides.sortDescriptors = [sortOverride]
- requestOverrides.fetchLimit = 1
- try? overrideArray = coredataContext.fetch(requestOverrides)
-
- let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest
- let sortTT = NSSortDescriptor(key: "date", ascending: false)
- requestTempTargets.sortDescriptors = [sortTT]
- requestTempTargets.fetchLimit = 1
- try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
- }
-
- isPercentageEnabled = booleanArray.first?.enabled ?? false
- useOverride = overrideArray.first?.enabled ?? false
- overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
- unlimited = overrideArray.first?.indefinite ?? true
- hbtSetting = Decimal(booleanArray.first?.hbt ?? 160)
-
- if useOverride {
- duration = (overrideArray.first?.duration ?? 0) as Decimal
- let addedMinutes = Int(duration)
- let date = overrideArray.first?.date ?? Date()
- if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
- !unlimited
- { useOverride = false }
-
- newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
- }
-
- if newDuration < 0 {
- newDuration = 0
- } else { duration = newDuration }
-
- if !useOverride {
- unlimited = true
- overridePercentage = 100
- duration = 0
- }
-
- if tempTargetsArray.first?.active ?? false || booleanArray.first?.enabled ?? false {
- var duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
- var hbt = tempTargetsArray.first?.hbt ?? 160
- if booleanArray.first?.enabled ?? false, !(booleanArray.first?.isPreset ?? false) {
- duration_ = Int(truncating: booleanArray.first?.duration ?? 0)
- hbt = booleanArray.first?.hbt ?? 160
- }
- let startDate = tempTargetsArray.first?.startDate ?? Date()
- let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
- let dd = durationPlusStart.timeIntervalSinceNow.minutes
-
- if dd > 0 {
- hbtSetting = Decimal(hbt)
- isPercentageEnabled = true
- } else { isPercentageEnabled = false }
- }
-
- let averages = Oref2_variables(
- average_total_data: 0,
- weightedAverage: 1,
- past2hoursAverage: 0,
- date: Date(),
- isEnabled: isPercentageEnabled,
- overridePercentage: overridePercentage,
- useOverride: useOverride,
- duration: duration,
- unlimited: unlimited,
- hbt: hbtSetting
- )
- storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
- }
-
- coredataContext.performAndWait {
- let saveNewUseOverride = Override(context: self.coredataContext)
- saveNewUseOverride.date = Date()
- saveNewUseOverride.enabled = useOverride
- saveNewUseOverride.percentage = Double(overridePercentage)
- saveNewUseOverride.duration = newDuration as NSDecimalNumber
- saveNewUseOverride.indefinite = unlimited
- try? self.coredataContext.save()
- }
- }
-
private func roundDecimal(_ decimal: Decimal, _ digits: Double) -> Decimal {
let rounded = round(Double(decimal) * pow(10, digits)) / pow(10, digits)
return Decimal(rounded)
@@ -1258,9 +1036,10 @@ final class BaseAPSManager: APSManager, Injectable {
lastIndex = true
}
if array[i].bg_ < Double(hypoLimit), !lastIndex {
- timeInHypo += (currentTime - previousTime).timeInterval
+ // Exclude duration between CGM readings which are more than 30 minutes
+ timeInHypo += min((currentTime - previousTime).timeInterval, 30.minutes.timeInterval)
} else if array[i].bg_ >= Double(hyperLimit), !lastIndex {
- timeInHyper += (currentTime - previousTime).timeInterval
+ timeInHyper += min((currentTime - previousTime).timeInterval, 30.minutes.timeInterval)
}
}
if timeInHypo == 0.0 {
diff --git a/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift b/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
index c620c35043..26837cd97e 100644
--- a/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
+++ b/FreeAPS/Sources/APS/OpenAPS/OpenAPS.swift
@@ -9,7 +9,7 @@ final class OpenAPS {
private let storage: FileStorage
- let coredataContext = CoreDataStack.shared.persistentContainer.newBackgroundContext()
+ let coredataContext = CoreDataStack.shared.persistentContainer.viewContext // newBackgroundContext()
init(storage: FileStorage) {
self.storage = storage
@@ -44,8 +44,6 @@ final class OpenAPS {
self.storage.save(meal, as: Monitor.meal)
- let oref2_variables = self.loadFileFromStorage(name: OpenAPS.Monitor.oref2_variables)
-
// iob
let autosens = self.loadFileFromStorage(name: Settings.autosense)
let iob = self.iob(
@@ -62,6 +60,9 @@ final class OpenAPS {
let preferences = self.loadFileFromStorage(name: Settings.preferences)
+ // oref2
+ let oref2_variables = self.oref2()
+
let suggested = self.determineBasal(
glucose: glucose,
currentTemp: tempBasal,
@@ -117,6 +118,183 @@ final class OpenAPS {
}
}
+ func oref2() -> RawJSON {
+ coredataContext.performAndWait {
+ let now = Date()
+ let preferences = storage.retrieve(OpenAPS.Settings.preferences, as: Preferences.self)
+ var hbt_ = preferences?.halfBasalExerciseTarget ?? 160
+ let wp = preferences?.weightPercentage ?? 1
+
+ let tenDaysAgo = Date().addingTimeInterval(-10.days.timeInterval)
+ let twoHoursAgo = Date().addingTimeInterval(-2.hours.timeInterval)
+
+ var uniqueEvents = [TDD]()
+ let requestTDD = TDD.fetchRequest() as NSFetchRequest
+ requestTDD.predicate = NSPredicate(format: "timestamp > %@ AND tdd > 0", tenDaysAgo as NSDate)
+ let sortTDD = NSSortDescriptor(key: "timestamp", ascending: true)
+ requestTDD.sortDescriptors = [sortTDD]
+ try? uniqueEvents = coredataContext.fetch(requestTDD)
+
+ var sliderArray = [TempTargetsSlider]()
+ let requestIsEnbled = TempTargetsSlider.fetchRequest() as NSFetchRequest
+ let sortIsEnabled = NSSortDescriptor(key: "date", ascending: false)
+ requestIsEnbled.sortDescriptors = [sortIsEnabled]
+ // requestIsEnbled.fetchLimit = 1
+ try? sliderArray = coredataContext.fetch(requestIsEnbled)
+
+ var overrideArray = [Override]()
+ let requestOverrides = Override.fetchRequest() as NSFetchRequest
+ let sortOverride = NSSortDescriptor(key: "date", ascending: false)
+ requestOverrides.sortDescriptors = [sortOverride]
+ requestOverrides.fetchLimit = 1
+ try? overrideArray = coredataContext.fetch(requestOverrides)
+
+ var tempTargetsArray = [TempTargets]()
+ let requestTempTargets = TempTargets.fetchRequest() as NSFetchRequest
+ let sortTT = NSSortDescriptor(key: "date", ascending: false)
+ requestTempTargets.sortDescriptors = [sortTT]
+ requestTempTargets.fetchLimit = 1
+ try? tempTargetsArray = coredataContext.fetch(requestTempTargets)
+
+ let total = uniqueEvents.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
+ var indeces = uniqueEvents.count
+ // Only fetch once. Use same (previous) fetch
+ let twoHoursArray = uniqueEvents.filter({ ($0.timestamp ?? Date()) >= twoHoursAgo })
+ var nrOfIndeces = twoHoursArray.count
+ let totalAmount = twoHoursArray.compactMap({ each in each.tdd as? Decimal ?? 0 }).reduce(0, +)
+
+ var temptargetActive = tempTargetsArray.first?.active ?? false
+ var isPercentageEnabled = sliderArray.first?.enabled ?? false
+
+ var useOverride = overrideArray.first?.enabled ?? false
+ var overridePercentage = Decimal(overrideArray.first?.percentage ?? 100)
+ var unlimited = overrideArray.first?.indefinite ?? true
+
+ let currentTDD = (uniqueEvents.last?.tdd ?? 0) as Decimal
+
+ if indeces == 0 {
+ indeces = 1
+ }
+ if nrOfIndeces == 0 {
+ nrOfIndeces = 1
+ }
+
+ let average2hours = totalAmount / Decimal(nrOfIndeces)
+ let average14 = total / Decimal(indeces)
+
+ let weight = wp
+ let weighted_average = weight * average2hours + (1 - weight) * average14
+
+ var duration: Decimal = 0
+ var newDuration: Decimal = 0
+
+ if useOverride {
+ duration = (overrideArray.first?.duration ?? 0) as Decimal
+ let addedMinutes = Int(duration)
+ let date = overrideArray.first?.date ?? Date()
+ if date.addingTimeInterval(addedMinutes.minutes.timeInterval) < Date(),
+ !unlimited
+ {
+ useOverride = false
+ let saveToCoreData = Override(context: self.coredataContext)
+ saveToCoreData.enabled = false
+ saveToCoreData.date = Date()
+ saveToCoreData.duration = 0
+ saveToCoreData.indefinite = false
+ saveToCoreData.percentage = Double(overridePercentage)
+ try? self.coredataContext.save()
+ } else if overrideArray.first?.indefinite ?? false {
+ let saveToCoreData = Override(context: self.coredataContext)
+ saveToCoreData.enabled = true
+ saveToCoreData.date = Date()
+ saveToCoreData.duration = 0
+ saveToCoreData.indefinite = true
+ saveToCoreData.percentage = Double(overridePercentage)
+ try? self.coredataContext.save()
+ } else {
+ newDuration = Decimal(Date().distance(to: date.addingTimeInterval(addedMinutes.minutes.timeInterval)).minutes)
+ let saveToCoreData = Override(context: self.coredataContext)
+ saveToCoreData.enabled = true
+ saveToCoreData.date = Date()
+ saveToCoreData.duration = newDuration as NSDecimalNumber
+ saveToCoreData.indefinite = false
+ saveToCoreData.percentage = Double(overridePercentage)
+ try? self.coredataContext.save()
+ }
+ }
+
+ if newDuration < 0 {
+ newDuration = 0
+ } else {
+ duration = newDuration
+ }
+
+ if !useOverride {
+ unlimited = true
+ overridePercentage = 100
+ duration = 0
+ }
+
+ if temptargetActive /* || isPercentageEnabled */ {
+ var duration_ = 0
+ var hbt = Double(hbt_)
+ var dd = 0.0
+
+ if temptargetActive {
+ duration_ = Int(truncating: tempTargetsArray.first?.duration ?? 0)
+ hbt = tempTargetsArray.first?.hbt ?? Double(hbt_)
+ let startDate = tempTargetsArray.first?.startDate ?? Date()
+ let durationPlusStart = startDate.addingTimeInterval(duration_.minutes.timeInterval)
+ dd = durationPlusStart.timeIntervalSinceNow.minutes
+
+ if dd > 0.1 {
+ hbt_ = Decimal(hbt)
+ // isPercentageEnabled = false
+ temptargetActive = true
+ } else {
+ temptargetActive = false
+ }
+ }
+ }
+
+ if currentTDD > 0 {
+ let averages = Oref2_variables(
+ average_total_data: average14,
+ weightedAverage: weighted_average,
+ past2hoursAverage: average2hours,
+ date: Date(),
+ isEnabled: temptargetActive,
+ presetActive: isPercentageEnabled,
+ overridePercentage: overridePercentage,
+ useOverride: useOverride,
+ duration: duration,
+ unlimited: unlimited,
+ hbt: hbt_
+ )
+ storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
+ print("Test time for oref2_variables: \(-now.timeIntervalSinceNow) seconds")
+ return self.loadFileFromStorage(name: Monitor.oref2_variables)
+
+ } else {
+ let averages = Oref2_variables(
+ average_total_data: 0,
+ weightedAverage: 1,
+ past2hoursAverage: 0,
+ date: Date(),
+ isEnabled: temptargetActive,
+ presetActive: isPercentageEnabled,
+ overridePercentage: overridePercentage,
+ useOverride: useOverride,
+ duration: duration,
+ unlimited: unlimited,
+ hbt: hbt_
+ )
+ storage.save(averages, as: OpenAPS.Monitor.oref2_variables)
+ return self.loadFileFromStorage(name: Monitor.oref2_variables)
+ }
+ }
+ }
+
func autosense() -> Future {
Future { promise in
self.processQueue.async {
diff --git a/FreeAPS/Sources/Models/Oref2_variables.swift b/FreeAPS/Sources/Models/Oref2_variables.swift
index 92d49f9454..638709016e 100644
--- a/FreeAPS/Sources/Models/Oref2_variables.swift
+++ b/FreeAPS/Sources/Models/Oref2_variables.swift
@@ -6,6 +6,7 @@ struct Oref2_variables: JSON, Equatable {
var past2hoursAverage: Decimal
var date: Date
var isEnabled: Bool
+ var presetActive: Bool
var overridePercentage: Decimal
var useOverride: Bool
var duration: Decimal
@@ -18,6 +19,7 @@ struct Oref2_variables: JSON, Equatable {
past2hoursAverage: Decimal,
date: Date,
isEnabled: Bool,
+ presetActive: Bool,
overridePercentage: Decimal,
useOverride: Bool,
duration: Decimal,
@@ -29,6 +31,7 @@ struct Oref2_variables: JSON, Equatable {
self.past2hoursAverage = past2hoursAverage
self.date = date
self.isEnabled = isEnabled
+ self.presetActive = presetActive
self.overridePercentage = overridePercentage
self.useOverride = useOverride
self.duration = duration
@@ -44,6 +47,7 @@ extension Oref2_variables {
case past2hoursAverage
case date
case isEnabled
+ case presetActive
case overridePercentage
case useOverride
case duration
diff --git a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift
index 4f217614bb..1158476078 100644
--- a/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift
+++ b/FreeAPS/Sources/Modules/AddCarbs/AddCarbsStateModel.swift
@@ -1,4 +1,5 @@
import CoreData
+import Foundation
import SwiftUI
extension AddCarbs {
@@ -128,13 +129,6 @@ extension AddCarbs {
if a != nil, summation[a ?? 0] != "" {
summation.remove(at: a!)
}
- if (selection?.carbs ?? 0) as Decimal == carbs, (selection?.fat ?? 0) as Decimal == fat,
- (selection?.protein ?? 0) as Decimal == protein
- {
- carbs = 0
- fat = 0
- protein = 0
- }
}
func addPresetToNewMeal() {
@@ -144,9 +138,85 @@ extension AddCarbs {
}
}
- func fullMeal() -> [String] {
- let filteredArray = summation.filter { !$0.isEmpty }
- return filteredArray
+ func addNewPresetToWaitersNotepad(_ dish: String) {
+ summation.append(dish)
+ }
+
+ func addToSummation() {
+ summation.append(selection?.dish ?? "")
+ }
+
+ func waitersNotepad() -> String {
+ var filteredArray = summation.filter { !$0.isEmpty }
+
+ if carbs == 0, protein == 0, fat == 0 {
+ filteredArray = []
+ }
+
+ guard filteredArray != [] else {
+ return ""
+ }
+ var carbs_: Decimal = 0.0
+ var fat_: Decimal = 0.0
+ var protein_: Decimal = 0.0
+ var presetArray = [Presets]()
+
+ coredataContext.performAndWait {
+ let requestPresets = Presets.fetchRequest() as NSFetchRequest
+ try? presetArray = coredataContext.fetch(requestPresets)
+ }
+ var waitersNotepad = [String]()
+ var stringValue = ""
+
+ for each in filteredArray {
+ let countedSet = NSCountedSet(array: filteredArray)
+ let count = countedSet.count(for: each)
+ if each != stringValue {
+ waitersNotepad.append("\(count) \(each)")
+ }
+ stringValue = each
+
+ for sel in presetArray {
+ if sel.dish == each {
+ carbs_ += (sel.carbs)! as Decimal
+ fat_ += (sel.fat)! as Decimal
+ protein_ += (sel.protein)! as Decimal
+ break
+ }
+ }
+ }
+ let extracarbs = carbs - carbs_
+ let extraFat = fat - fat_
+ let extraProtein = protein - protein_
+ var addedString = ""
+
+ if extracarbs > 0, filteredArray.isNotEmpty {
+ addedString += "Additional carbs: \(extracarbs) "
+ } else if extracarbs < 0 { addedString += "Removed carbs: \(extracarbs) " }
+
+ if extraFat > 0, filteredArray.isNotEmpty {
+ addedString += "Additional fat: \(extraFat) "
+ } else if extraFat < 0 { addedString += "Removed fat: \(extraFat) " }
+
+ if extraProtein > 0, filteredArray.isNotEmpty {
+ addedString += "Additional protein: \(extraProtein) "
+ } else if extraProtein < 0 { addedString += "Removed protein: \(extraProtein) " }
+
+ if addedString != "" {
+ waitersNotepad.append(addedString)
+ }
+ var waitersNotepadString = ""
+
+ if waitersNotepad.count == 1 {
+ waitersNotepadString = waitersNotepad[0]
+ } else if waitersNotepad.count > 1 {
+ for each in waitersNotepad {
+ if each != waitersNotepad.last {
+ waitersNotepadString += " " + each + ","
+ } else { waitersNotepadString += " " + each }
+ }
+ }
+ return waitersNotepadString
}
}
}
diff --git a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
index 29422cba89..0a7701784c 100644
--- a/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
+++ b/FreeAPS/Sources/Modules/AddCarbs/View/AddCarbsRootView.swift
@@ -86,7 +86,7 @@ extension AddCarbs {
DatePicker("Date", selection: $state.date)
}
- Section(footer: Text(state.fullMeal().description != "[]" ? state.fullMeal().description : "")) {
+ Section(footer: Text(state.waitersNotepad().description)) {
Button { state.add() }
label: { Text("Save and continue") }
.disabled(state.carbs <= 0 && state.fat <= 0 && state.protein <= 0)
@@ -138,7 +138,7 @@ extension AddCarbs {
state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
- state.summation.append(state.selection?.dish ?? "")
+ state.addToSummation()
}
}
HStack {
@@ -155,6 +155,12 @@ extension AddCarbs {
Button("No", role: .cancel) {}
Button("Yes", role: .destructive) {
state.deletePreset()
+
+ state.carbs += ((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal
+ state.fat += ((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal
+ state.protein += ((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal
+
+ state.addPresetToNewMeal()
}
}
)
@@ -178,14 +184,17 @@ extension AddCarbs {
} else { state.protein = 0 }
state.removePresetFromNewMeal()
+ if state.carbs == 0, state.fat == 0, state.protein == 0 { state.summation = [] }
+
}
label: { Text("[ -1 ]") }
- .disabled(state.selection == nil || (
- (((state.selection?.carbs ?? 0) as NSDecimalNumber) as Decimal) == state
- .carbs && (((state.selection?.fat ?? 0) as NSDecimalNumber) as Decimal) == state
- .fat && (((state.selection?.protein ?? 0) as NSDecimalNumber) as Decimal) == state
- .protein
- ))
+ .disabled(
+ state
+ .selection == nil ||
+ (
+ !state.summation.contains(state.selection?.dish ?? "") && (state.selection?.dish ?? "") != ""
+ )
+ )
.buttonStyle(BorderlessButtonStyle())
.frame(maxWidth: .infinity, alignment: .trailing)
.accentColor(.minus)
diff --git a/FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift b/FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift
index 3cbcc42c7c..77d3480653 100644
--- a/FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift
+++ b/FreeAPS/Sources/Modules/AddTempTarget/AddTempTargetStateModel.swift
@@ -38,13 +38,30 @@ extension AddTempTarget {
if viewPercantage {
lowTarget = computeTarget()
+ coredataContext.performAndWait {
+ let saveToCoreData = TempTargets(context: self.coredataContext)
+ saveToCoreData.id = UUID().uuidString
+ saveToCoreData.active = true
+ saveToCoreData.hbt = hbt
+ saveToCoreData.date = Date()
+ saveToCoreData.duration = duration as NSDecimalNumber
+ saveToCoreData.startDate = Date()
+ try? self.coredataContext.save()
+ }
saveSettings = true
+ } else {
+ coredataContext.performAndWait {
+ let saveToCoreData = TempTargets(context: coredataContext)
+ saveToCoreData.active = false
+ saveToCoreData.date = Date()
+ try? coredataContext.save()
+ }
}
var highTarget = lowTarget
if units == .mmolL, !viewPercantage {
- lowTarget = lowTarget.asMgdL
- highTarget = highTarget.asMgdL
+ lowTarget = Decimal(round(Double(lowTarget.asMgdL)))
+ highTarget = lowTarget
}
let entry = TempTarget(
@@ -72,6 +89,8 @@ extension AddTempTarget {
let setHBT = TempTargetsSlider(context: self.coredataContext)
setHBT.enabled = false
+ setHBT.date = Date()
+
try? self.coredataContext.save()
}
}
@@ -90,8 +109,8 @@ extension AddTempTarget {
var highTarget = lowTarget
if units == .mmolL, !viewPercantage {
- lowTarget = lowTarget.asMgdL
- highTarget = highTarget.asMgdL
+ lowTarget = Decimal(round(Double(lowTarget.asMgdL)))
+ highTarget = lowTarget
}
let entry = TempTarget(
@@ -115,7 +134,6 @@ extension AddTempTarget {
saveToCoreData.isPreset = true
saveToCoreData.enabled = true
saveToCoreData.hbt = hbt
- saveToCoreData.enabled = true
saveToCoreData.date = Date()
saveToCoreData.duration = duration as NSDecimalNumber
try? self.coredataContext.save()
diff --git a/FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift b/FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift
index 8b02c2f387..f7f1c83cc6 100644
--- a/FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift
+++ b/FreeAPS/Sources/Modules/AddTempTarget/View/AddTempTargetRootView.swift
@@ -147,23 +147,6 @@ extension AddTempTarget {
.navigationTitle("Enact Temp Target")
.navigationBarTitleDisplayMode(.automatic)
.navigationBarItems(leading: Button("Close", action: state.hideModal))
- .onDisappear {
- if state.viewPercantage, state.saveSettings {
- let isEnabledMoc = TempTargetsSlider(context: moc)
- isEnabledMoc.enabled = true
- isEnabledMoc.date = Date()
- isEnabledMoc.hbt = state.hbt
- isEnabledMoc.duration = state.duration as NSDecimalNumber
- isEnabledMoc.isPreset = false
- try? moc.save()
- } else {
- let isEnabledMoc = TempTargetsSlider(context: moc)
- isEnabledMoc.enabled = false
- isEnabledMoc.date = Date()
- // isEnabledMoc.hbt = isEnabledArray.first?.hbt ?? 160
- try? moc.save()
- }
- }
}
private func presetView(for preset: TempTarget) -> some View {
diff --git a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift
index 717d8e8c12..9e07d92a61 100644
--- a/FreeAPS/Sources/Modules/Home/HomeStateModel.swift
+++ b/FreeAPS/Sources/Modules/Home/HomeStateModel.swift
@@ -54,6 +54,7 @@ extension Home {
@Published var animatedBackground = false
@Published var manualTempBasal = false
@Published var smooth = false
+ @Published var maxValue: Decimal = 1.2
override func subscribe() {
setupGlucose()
@@ -85,6 +86,7 @@ extension Home {
setStatusTitle()
setupCurrentTempTarget()
smooth = settingsManager.settings.smoothGlucose
+ maxValue = settingsManager.preferences.autosensMax
broadcaster.register(GlucoseObserver.self, observer: self)
broadcaster.register(SuggestionObserver.self, observer: self)
@@ -351,6 +353,15 @@ extension Home {
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
+
+ func infoPanelTTPercentage(_ hbt_: Double, _ target: Decimal) -> Decimal {
+ guard hbt_ != 0 || target != 0 else {
+ return 0
+ }
+ let c = Decimal(hbt_ - 100)
+ let ratio = min(c / (target + c - 100), maxValue)
+ return (ratio * 100)
+ }
}
}
diff --git a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
index 5bef4b2335..a51b897a6f 100644
--- a/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
+++ b/FreeAPS/Sources/Modules/Home/View/HomeRootView.swift
@@ -31,6 +31,16 @@ extension Home {
sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
) var fetchedPercent: FetchedResults
+ @FetchRequest(
+ entity: TempTargets.entity(),
+ sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+ ) var sliderTTpresets: FetchedResults
+
+ @FetchRequest(
+ entity: TempTargetsSlider.entity(),
+ sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)]
+ ) var enactedSliderTT: FetchedResults
+
private var numberFormatter: NumberFormatter {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
@@ -162,93 +172,82 @@ extension Home {
}
}
+ var tempBasalString: String? {
+ guard let tempRate = state.tempRate else {
+ return nil
+ }
+ let rateString = numberFormatter.string(from: tempRate as NSNumber) ?? "0"
+ var manualBasalString = ""
+
+ if state.apsManager.isManualTempBasal {
+ manualBasalString = NSLocalizedString(
+ " - Manual Basal ⚠️",
+ comment: "Manual Temp basal"
+ )
+ }
+ return rateString + NSLocalizedString(" U/hr", comment: "Unit per hour with space") + manualBasalString
+ }
+
+ var tempTargetString: String? {
+ guard let tempTarget = state.tempTarget else {
+ return nil
+ }
+ let target = tempTarget.targetBottom ?? 0
+ let unitString = targetFormatter.string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber) ?? ""
+ let rawString = (tirFormatter.string(from: (tempTarget.targetBottom ?? 0) as NSNumber) ?? "") + " " + state.units
+ .rawValue
+
+ var string = ""
+ if sliderTTpresets.first?.active ?? false {
+ let hbt = sliderTTpresets.first?.hbt ?? 0
+ string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
+ } /* else if enactedSliderTT.first?.enabled ?? false {
+ let hbt = enactedSliderTT.first?.hbt ?? 0
+ string = ", " + (tirFormatter.string(from: state.infoPanelTTPercentage(hbt, target) as NSNumber) ?? "") + " %"
+ } */
+
+ let percentString = state
+ .units == .mmolL ? (unitString + " mmol/L" + string) : (rawString + (string == "0" ? "" : string))
+ return tempTarget.displayName + " " + percentString
+ }
+
+ var overrideString: String? {
+ guard fetchedPercent.first?.enabled ?? false else {
+ return nil
+ }
+ let percentString = "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) %"
+ let durationString = (fetchedPercent.first?.indefinite ?? false) ?
+ "" : ", " + (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
+
+ return percentString + durationString
+ }
+
var infoPanel: some View {
HStack(alignment: .center) {
if state.pumpSuspended {
Text("Pump suspended")
.font(.system(size: 12, weight: .bold)).foregroundColor(.loopGray)
.padding(.leading, 8)
- } else if let tempRate = state.tempRate {
- if state.apsManager.isManualTempBasal {
- Text(
- (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
- NSLocalizedString(" U/hr", comment: "Unit per hour with space") +
- NSLocalizedString(" - Manual Basal ⚠️", comment: "Manual Temp basal")
- )
- .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
- .padding(.leading, 8)
- } else {
- Text(
- (numberFormatter.string(from: tempRate as NSNumber) ?? "0") +
- NSLocalizedString(" U/hr", comment: "Unit per hour with space")
- )
- .font(.system(size: 12, weight: .bold)).foregroundColor(.insulin)
+ } else if let tempBasalString = tempBasalString {
+ Text(tempBasalString)
+ .font(.system(size: 12, weight: .bold))
+ .foregroundColor(.insulin)
.padding(.leading, 8)
- }
}
- if let tempTarget = state.tempTarget {
- Text(tempTarget.displayName).font(.caption).foregroundColor(.secondary)
- if state.units == .mmolL {
- Text(
- targetFormatter
- .string(from: (tempTarget.targetBottom?.asMmolL ?? 0) as NSNumber)!
- )
+ if let tempTargetString = tempTargetString {
+ Text(tempTargetString)
.font(.caption)
.foregroundColor(.secondary)
- if tempTarget.targetBottom != tempTarget.targetTop {
- Text("-").font(.caption)
- .foregroundColor(.secondary)
- Text(
- targetFormatter
- .string(from: (tempTarget.targetTop?.asMmolL ?? 0) as NSNumber)! +
- " \(state.units.rawValue)"
- )
- .font(.caption)
- .foregroundColor(.secondary)
- } else {
- Text(state.units.rawValue).font(.caption)
- .foregroundColor(.secondary)
- }
-
- } else {
- Text(targetFormatter.string(from: (tempTarget.targetBottom ?? 0) as NSNumber)!)
- .font(.caption)
- .foregroundColor(.secondary)
- if tempTarget.targetBottom != tempTarget.targetTop {
- Text("-").font(.caption)
- .foregroundColor(.secondary)
- Text(
- targetFormatter
- .string(from: (tempTarget.targetTop ?? 0) as NSNumber)! + " \(state.units.rawValue)"
- )
- .font(.caption)
- .foregroundColor(.secondary)
- } else {
- Text(state.units.rawValue).font(.caption)
- .foregroundColor(.secondary)
- }
- }
}
Spacer()
-
- Text(
- (fetchedPercent.first?.enabled ?? false) ?
- "\((fetchedPercent.first?.percentage ?? 100).formatted(.number)) % " : ""
- )
- .font(.system(size: 12, weight: .bold))
- .foregroundColor(.orange)
- .padding(.trailing, 2)
- if fetchedPercent.first?.enabled ?? false {
- Text(
- (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") == "0" ?
- "Perpetual" :
- (tirFormatter.string(from: (fetchedPercent.first?.duration ?? 0) as NSNumber) ?? "") + " min"
- )
- .font(.system(size: 12))
- .foregroundColor(.orange)
- .padding(.trailing, 8)
+
+ if let overrideString = overrideString {
+ Text(overrideString)
+ .font(.system(size: 12))
+ .foregroundColor(.orange)
+ .padding(.trailing, 8)
}
if let progress = state.bolusProgress {
diff --git a/FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift b/FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift
index 56de0b6d7e..756f17117b 100644
--- a/FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift
+++ b/FreeAPS/Sources/Modules/OverrideProfilesConfig/View/OverrideProfilesRootView.swift
@@ -52,9 +52,13 @@ extension OverrideProfilesConfig {
onEditingChanged: { editing in
isEditing = editing
}
- )
+ ).accentColor(state.percentage >= 130 ? .red : .blue)
Text("\(state.percentage.formatted(.number)) %")
- .foregroundColor(isEditing ? .orange : .blue)
+ .foregroundColor(
+ state
+ .percentage >= 130 ? .red :
+ (isEditing ? .orange : .blue)
+ )
.font(.largeTitle)
Spacer()
Toggle(isOn: $state._indefinite) {
@@ -81,7 +85,9 @@ extension OverrideProfilesConfig {
.frame(maxWidth: .infinity, alignment: .center)
.controlSize(.mini)
.alert(
- "Saving this override will change your basal insulin, ISF and CR during the entire selected duration. Tapping save will start your new overide or edit your current active override.",
+ "Selected Override:\n\n\(state.percentage.formatted(.number)) %, " +
+ (state.duration > 0 ? "\(state.duration) min" : " infinite duration.") + "\n\n" +
+ "Saving this override will change your basal insulin, ISF and CR during the entire selected duration. Tapping save will start your new overide or edit your current active override.",
isPresented: $showAlert,
actions: {
Button("Cancel", role: .cancel) {}
diff --git a/FreeAPSWatch WatchKit Extension/Views/CarbsView.swift b/FreeAPSWatch WatchKit Extension/Views/CarbsView.swift
index 8865e87f34..3af277aef0 100644
--- a/FreeAPSWatch WatchKit Extension/Views/CarbsView.swift
+++ b/FreeAPSWatch WatchKit Extension/Views/CarbsView.swift
@@ -60,7 +60,7 @@ struct CarbsView: View {
.renderingMode(.template)
.resizable()
.frame(width: 24, height: 24)
- .foregroundColor(.loopGreen)
+ .foregroundColor(.loopYellow)
Text("Add Carbs ")
}
}
diff --git a/FreeAPSWatch WatchKit Extension/Views/MainView.swift b/FreeAPSWatch WatchKit Extension/Views/MainView.swift
index ea3264f463..79f431feb7 100644
--- a/FreeAPSWatch WatchKit Extension/Views/MainView.swift
+++ b/FreeAPSWatch WatchKit Extension/Views/MainView.swift
@@ -102,10 +102,9 @@ struct MainView: View {
.scaledToFill()
.foregroundColor(Color.white)
.minimumScaleFactor(0.5)
- Text("g").foregroundColor(.loopGreen)
+ Text("g").foregroundColor(.loopYellow)
.font(.caption2)
.scaledToFill()
- .foregroundColor(.loopGreen)
.minimumScaleFactor(0.5)
Spacer()
Text(iobFormatter.string(from: (state.iob ?? 0) as NSNumber)!)
@@ -117,7 +116,6 @@ struct MainView: View {
Text("U").foregroundColor(.insulin)
.font(.caption2)
.scaledToFill()
- .foregroundColor(.loopGreen)
.minimumScaleFactor(0.5)
if state.displayHR {
@@ -220,7 +218,7 @@ struct MainView: View {
.renderingMode(.template)
.resizable()
.frame(width: 24, height: 24)
- .foregroundColor(.loopGreen)
+ .foregroundColor(.loopYellow)
}
NavigationLink(isActive: $state.isTempTargetViewActive) {
@@ -232,7 +230,7 @@ struct MainView: View {
.renderingMode(.template)
.resizable()
.frame(width: 24, height: 24)
- .foregroundColor(.loopYellow)
+ .foregroundColor(.loopGreen)
if let until = state.tempTargets.compactMap(\.until).first, until > Date() {
Text(until, style: .timer)
.scaledToFill()