From e3288c9353c5969922f0d06d21cca6be52d8585d Mon Sep 17 00:00:00 2001 From: Joseph Abbey Date: Sat, 27 Jan 2024 13:11:24 +0000 Subject: [PATCH 01/16] GPS and current activity in background service --- manifest-widget.xml | 212 +++++++++++++-------------- manifest.xml | 217 ++++++++++++++-------------- source/BackgroundServiceDelegate.mc | 40 ++++- source/WebhookManager.mc | 56 ++++--- 4 files changed, 293 insertions(+), 232 deletions(-) diff --git a/manifest-widget.xml b/manifest-widget.xml index 227f805..4683d5f 100644 --- a/manifest-widget.xml +++ b/manifest-widget.xml @@ -25,7 +25,8 @@ Use "Monkey C: Edit Application" from the Visual Studio Code command palette to update the application attributes. --> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - + \ No newline at end of file diff --git a/manifest.xml b/manifest.xml index 1d58e48..90e439a 100644 --- a/manifest.xml +++ b/manifest.xml @@ -28,9 +28,11 @@ Testing in VSCode requires monkey.jungle, so for convenience, swap between watch-app and widget by changing which of the next two lines are commented out --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + - + \ No newline at end of file diff --git a/source/BackgroundServiceDelegate.mc b/source/BackgroundServiceDelegate.mc index a1746cb..7876d8b 100644 --- a/source/BackgroundServiceDelegate.mc +++ b/source/BackgroundServiceDelegate.mc @@ -38,12 +38,43 @@ class BackgroundServiceDelegate extends System.ServiceDelegate { } function onTemporalEvent() as Void { - if (! System.getDeviceSettings().phoneConnected) { + if (!System.getDeviceSettings().phoneConnected) { // System.println("BackgroundServiceDelegate onTemporalEvent(): No Phone connection, skipping API call."); - } else if (! System.getDeviceSettings().connectionAvailable) { + } else if (!System.getDeviceSettings().connectionAvailable) { // System.println("BackgroundServiceDelegate onTemporalEvent(): No Internet connection, skipping API call."); } else { + // System.println("BackgroundServiceDelegate onTemporalEvent(): Making API call."); + var position = Position.getInfo(); + // System.println("BackgroundServiceDelegate onTemporalEvent(): gps: " + position.position.toDegrees()); + // System.println("BackgroundServiceDelegate onTemporalEvent(): speed: " + position.speed); + // System.println("BackgroundServiceDelegate onTemporalEvent(): course: " + position.heading + "rad (" + (position.heading * 180 / Math.PI) + "°)"); + // System.println("BackgroundServiceDelegate onTemporalEvent(): altitude: " + position.altitude); + // System.println("BackgroundServiceDelegate onTemporalEvent(): battery: " + System.getSystemStats().battery); + // System.println("BackgroundServiceDelegate onTemporalEvent(): charging: " + System.getSystemStats().charging); + // System.println("BackgroundServiceDelegate onTemporalEvent(): activity: " + Activity.getProfileInfo().name); + // Don't use Settings.* here as the object lasts < 30 secs and is recreated each time the background service is run + Communications.makeWebRequest( + (Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String), + { + "type" => "update_location", + "data" => { + "gps" => position.position.toDegrees(), + "gps_accuracy" => 10, + "speed" => Math.round(position.speed), + "course" => Math.round(position.heading * 180 / Math.PI), + "altitude" => Math.round(position.altitude), + } + }, + { + :method => Communications.HTTP_REQUEST_METHOD_POST, + :headers => { + "Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON + }, + :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON + }, + method(:onReturnBatteryUpdate) + ); Communications.makeWebRequest( (Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String), { @@ -58,6 +89,11 @@ class BackgroundServiceDelegate extends System.ServiceDelegate { "state" => System.getSystemStats().charging, "type" => "binary_sensor", "unique_id" => "battery_is_charging" + }, + { + "state" => Activity.getProfileInfo().name, + "type" => "sensor", + "unique_id" => "activity" } ] }, diff --git a/source/WebhookManager.mc b/source/WebhookManager.mc index 3e38f72..3307484 100644 --- a/source/WebhookManager.mc +++ b/source/WebhookManager.mc @@ -62,10 +62,12 @@ class WebhookManager { ErrorView.show(WatchUi.loadResource($.Rez.Strings.WebhookFailed) as Lang.String + "\n" + WatchUi.loadResource($.Rez.Strings.ApiUrlNotFound) as Lang.String); break; + case 200: case 201: var id = data.get("webhook_id") as Lang.String or Null; if (id != null) { Settings.setWebhookId(id); + // System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering first sensor: Battery Level"); registerWebhookSensor({ "device_class" => "battery", "name" => "Battery Level", @@ -76,16 +78,7 @@ class WebhookManager { "state_class" => "measurement", "entity_category" => "diagnostic", "disabled" => false - }); - registerWebhookSensor({ - "device_class" => "battery_charging", - "name" => "Battery is Charging", - "state" => System.getSystemStats().charging, - "type" => "binary_sensor", - "unique_id" => "battery_is_charging", - "entity_category" => "diagnostic", - "disabled" => false - }); + }, 0); } else { // System.println("WebhookManager onReturnRequestWebhookId(): No webhook id in response data."); Settings.unsetIsBatteryLevelEnabled(); @@ -129,7 +122,7 @@ class WebhookManager { ); } - function onReturnRegisterWebhookSensor(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void { + function onReturnRegisterWebhookSensor(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String, step as Lang.Number) as Void { switch (responseCode) { case Communications.BLE_HOST_TIMEOUT: case Communications.BLE_CONNECTION_UNAVAILABLE: @@ -155,6 +148,7 @@ class WebhookManager { Settings.unsetWebhookId(); // Ignore and see if we can carry on break; + case Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE: // System.println("WebhookManager onReturnRegisterWebhookSensor() Response Code: INVALID_HTTP_BODY_IN_NETWORK_RESPONSE, check JSON is returned."); Settings.unsetWebhookId(); @@ -169,12 +163,37 @@ class WebhookManager { ErrorView.show(WatchUi.loadResource($.Rez.Strings.WebhookFailed) as Lang.String + "\n" + WatchUi.loadResource($.Rez.Strings.ApiUrlNotFound) as Lang.String); break; + case 200: case 201: - if ((data.get("success") as Lang.Boolean or Null) != true) { - // When uncommenting, invert the condition above. - // System.println("WebhookManager onReturnRegisterWebhookSensor(): Success"); - // } else { - // System.println("WebhookManager onReturnRegisterWebhookSensor(): Failure"); + if ((data.get("success") as Lang.Boolean or Null) != false) { + // System.println("WebhookManager onReturnRegisterWebhookSensor(): Success"); + switch (step) { + case 0: + // System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Battery is Charging"); + registerWebhookSensor({ + "device_class" => "battery_charging", + "name" => "Battery is Charging", + "state" => System.getSystemStats().charging, + "type" => "binary_sensor", + "unique_id" => "battery_is_charging", + "entity_category" => "diagnostic", + "disabled" => false + }, 1); + break; + case 1: + // System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Activity"); + registerWebhookSensor({ + "name" => "Activity", + "state" => Activity.getProfileInfo().name, + "type" => "sensor", + "unique_id" => "activity", + "disabled" => false + }, 2); + break; + default: + } + } else { + // System.println("WebhookManager onReturnRegisterWebhookSensor(): Failure"); Settings.unsetWebhookId(); Settings.unsetIsBatteryLevelEnabled(); ErrorView.show(WatchUi.loadResource($.Rez.Strings.WebhookFailed) as Lang.String); @@ -189,7 +208,7 @@ class WebhookManager { } } - function registerWebhookSensor(sensor as Lang.Object) { + function registerWebhookSensor(sensor as Lang.Object, step as Lang.Number) { // System.println("WebhookManager registerWebhookSensor(): Registering webhook sensor: " + sensor.toString()); Communications.makeWebRequest( Settings.getApiUrl() + "/webhook/" + Settings.getWebhookId(), @@ -202,7 +221,8 @@ class WebhookManager { :headers => { "Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON }, - :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON + :responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON, + :context => step }, method(:onReturnRegisterWebhookSensor) ); From 1ec80a17048413c505f8ff2d0110b6999bf4b389 Mon Sep 17 00:00:00 2001 From: Joseph Abbey Date: Sat, 27 Jan 2024 13:39:44 +0000 Subject: [PATCH 02/16] Update restyled.yml --- .github/restyled.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/restyled.yml b/.github/restyled.yml index c4eda48..f26d1c8 100644 --- a/.github/restyled.yml +++ b/.github/restyled.yml @@ -1,3 +1,5 @@ exclude: - '**/*.md' - '**/pnpm-lock.yaml' + - 'manifest.xml' + - 'manifest-widget.xml' From 4ff15090466d287cb2aae3b23667468d2a30bce8 Mon Sep 17 00:00:00 2001 From: Philip Abbey Date: Sat, 27 Jan 2024 13:57:21 +0000 Subject: [PATCH 03/16] API level fix Added 'has' test for Activity.getProfileInfo(). Reverted automatic changes to XML prettiness. Co-Authored-By: Joseph Abbey --- manifest-widget.xml | 3 +-- manifest.xml | 8 +++---- source/BackgroundServiceDelegate.mc | 37 ++++++++++++++++------------- source/WebhookManager.mc | 18 +++++++------- 4 files changed, 34 insertions(+), 32 deletions(-) diff --git a/manifest-widget.xml b/manifest-widget.xml index 4683d5f..cfa41e1 100644 --- a/manifest-widget.xml +++ b/manifest-widget.xml @@ -25,8 +25,7 @@ Use "Monkey C: Edit Application" from the Visual Studio Code command palette to update the application attributes. --> - + - - _Q|I=eBnrMT_IK6IByTQjV8g7oPus7 zPsX2Vc$S9c^dWY9*)!+D+5~J;_@?Su&D2*VHqg;G{!yW^xvS?W7_aPT{k`uDY|w;0 zpnw(Gk$-irzdyTu3vqG`7m(^1XX4c*1FMB=qFmlG+;@AGz#F#$ zVE8KRTV-dhW8q~$H9F8{jXBP8SihD~vkl{+I zTB~go^n=x5ch@mnrUn$p6c{_#F;|>Ya#<>K45Wmp>_;Ufu6l7rtdkj zw50z&RquTA>$3gJ_ymy`Z!gXflCIJj&)oh8kUJ7lEOTPgv1xh>A=E}Nz*i$yommEY z=K*5vhOXs~P}n)s2TT^B=G>*R>CEOWbSr_OSJ{xTubz-WW`f8ti{%7tM z91FOU2-iha!uXk>9gqT)YONbgbg@D`043 z#bFX=Zp%A1u#N1eS{d(AJ6Tz8^_HHY{3gSeV}z&H=YYaP*ct+=#er^20r+D8+d@fP z_>YcVvnThI=IP9z4rV!!-1j|S5zAj@EmNR4`#6$*G{QUsZz5 z>Lj<3)GfQ|%YnNv$z?B+GSlK8-WK|jt48*Qe7K<%QYtGTE8Bej?C+&bR}zvG4*k5r z9co)|I`X4wu6A-3U_lx8#k|(eB7d}Yp&SCWweQuL=}nF$QpHwcTka`ns64a3x|{W; z^S!~MhNNSnfXoE7Q~I*o#ZwBwhEa-GwMB>HNUAb;Coz}WK8~`#L z@LOkF7X6Cd@8}cPyI%!Vpdd6?fuT@@o^rUeUku8mDf%i&>2>iWJssT>>#egJ@I-`( z5FBb<_Ue6lMRO9At2lUvF=2lKam&6UWXIUK2XAe;-6BagakM$b~1He!wL|D&I~F zEsRA$Q;E9i9{m#hmK*EpyUfd$Bz}AfCp|nno&*xXXtsrLt_!K%^365^dD6RHCR}{@ zpU;V)cLM4lz{de0+N3N*>|;qaYFYAdAr={(a%U&6bZh0>8bQBxQFZ9TcU$;TDNakF zt7h{5MW?09!^3d$68=+5Ae)K&<=dg9-_7ywM*P~%fJM@S<*Rv?ULwEXr9u8aM~M7C zofQeLD-S-t;WqT&(??n9ePpXn}ITmHX(B&$rf=>PetvLv(o^AA7$a+804fTh7h_y4Jj z3teJVJp1pRf2ICz-&4i^eP5P#cy8;E+zT8GMHl81W+4q=A1q9kafW|h`*!6D|6d() zJy2e%vC1KmA)cUxt6F(lxrbE4@GxXKfAl z^pKwF;Ld6BQRae?s{ycfOZROh=e}A_`4?6l#`e&=Fj@2b^HPfK&byO?f z)UKnwWZkPr#vaa@J>vpumt4xwsynB^xh+(x|DVqorduSwxO6L1uFy8E{H)DU&Xnxhd3&x|Nh;^#n0&*w_+TTGN~-cBwg}nv&7fS zl0aLeqkyhrH%mN~m%@7cJJg*{lwUEVZos-(fXb-K&RS%!qH4NQ*TmiUOks%N%`lvX zN)#>=AYY<(>B4zp`u){~bb`^xhbO^TmLsz(5CMT4x=4yU5`Gck?rQ1T)Z8sT{Q)WK zgkavlB63YXUFV$_3OTSF5h@~ex1}LV+fF{6psLTJ==m}NX?&rY1ofLv1WN*iiE^W< zr4ze{_VP3@nG>-f^kJ@7L}sQ4neaxMnowIv(QcKu#N^S1u(OPU5h z$;|Da9u82tLtZktv0z~7XdTLuk5ETywom5%K8`6E+A_rP}{Yx5&15g z7q|cJc%3m<0W`cDxF01YGvMQv_CLc61P5=|nP#qc(}a@ha3*?c*);C)`#*MMP8cDe zfP|BMYKSQWztjpFJT1QvmDpR@&ODQcNpVaVz+qU9A5hT={N0`k-EGx{rGMSQ@;kCC zeEiPj-Po@OpYp=?N2Q?*)*r=iy#&U|}tSrxF4$&xQg z_}8v>v_l++k7!<*7#BOwHcf3P4V5_biTQWKSys}sghlJ0+2rClRY!GEKnb=X#mp-b z;UREAe0-$1g7J9NE> zUqF+@6h+)7qr(_$91oX6AgXRP2Mp#=Hkc<8G)6j=qkCADh!Rr;gc%@Mt{xAbNLNuj z%1_E6V{ME1>yFapFrRdfUP4Tx2GjMl4p6#+quJ!193%y^h=uN;E}P#qVsHDx6Ic!& zuO}~Tm=n#L!bGp9fc_BYIqY;y*csF24X3bzRR|G2lrv_AzT#-OD7@$3Zz~t?POP$M zCpT*ZtW_AHu8drbz|f39Z3`s?hOxB9t54&o*(2wtQs#MWQebY6fkfv9>sktHQ+(;s z=*xR%fUSSuBXc*16F~mngS)tr07mJF^9}%BY093Gh`J+O^MhQ3=rA%cCOdfH1N-Oa z)Hwg|L(*^yBFyL(AGE5#QbR{Vgn#iwKl86NJ5~h%R^5)PUw;>=E5=eqO zZM@!0a0*7PV}vt86u1MEkOFbobdp-}rya4e!fJbr=-rsK$~~P!Tvk#bIg6;EZ=;Vg zN%kuoU=Cu2_GZ$6nT!87@)Uxkv&=O5Y(%P z>#)U!O#FoAcT3LC5kZ*p{ldKpq6(0ZMyM;)DNGe`qzfSy(ueM4Ww)lN>NS`q- zZSXwrV&amPoM-^?w@Jpo>bFxLr&{x9ig0l5dE;&C`xB51b2gQh-1d~JixpN6ri zlk~G{gd=)o;Cx^Q%~!%XZRI={NOK3YK2FMB&OWL&jnGQvnD8x9b zy#*3a4AeajX_x^M#%16sa)2v~kZ1js&ew<))*`D>0mg0?*CwRt91wJ!$_7*F1G%7k z_U4SCG}cEmR^D&}DJUUzJY|G1h4B%A!*yNlS#?GnqzDg1@Y|jLH=HS7Lk~paD>b%> z>=nkKY%I-+@q%%wD`sX~?>9|mAo_d8TzAmG#Gi zb|pgC3&%I)!r=Mw)K9qoSXJE9N33mAJ2~>`_q5;?N zG&fR%9uQkCR+M!+IP|mLgv&Cq=;I}hkeuIlIdaTPRG}i|ppLFy$5m74y`NIdX1pXk zOF8>kcbTBYhZ53A4);LPM`uGtF+SvTr~D<(P5QSB9$7$6AAC|=qAVnBu!~bn#_Y^fzwFc# zv22?DIbJO`{>ccEVH6Bz5-B8Ab=eh8btR14lbLyEZvpuxQqbx5s=9F+;QSPX-2auk zO93TsD#aclSw55o3Pgx~|3$eM{ja~|o=K;SI;%+zE+G?WCL19Y*QnFZf%5Kr^?}h^ z-w_rcW#>=0Sz2AQ1QQZNO_>=U&^7k&Haz;Svo>S^NhpnYf1s{e4(L0=Z9&Y%vB}f0 z#|Ymj4S%*d1hhk!RK_~MLt9E|{dgtBfqD>1q2u6x7`oc=4eEYn82U?vaibWmg&?VcM~yMscX&58iMF@e_Oe6XMa&Nqd~3^lrFi zdscBDL>&9}c9Mc&AG)+fT>qBXpWdY5Z4$WM z_pE1Xwd8IE{1Np*AW1T_oh+vfa3~^q!)mr^9o%>M^B4w0ngQ=Zx7K zu9kxN@trUOJ>@!^c&6) zzw5$N?u}r?_k!84>}d}|@8OnfJvn-waD`@62S4u1R)6ule{@9$$nuxoQr2!pxBtGV zMh2<L)$AWG4rEXex~&)jf!8X6^D5elDfJ zroTU$E@#)((O?=$AL5N$(18#^0lHrzEoJR9bU@y zL)v-jQq8dC?M?GYknXgvB6jA*&J@MdysW`u)^S4GVvIKfE)aU<|NNu1Bw~r;B9O}~ z^f5TqN+TswzITe4qC&!Fht_4E0ZCp&GrWB0>$!we?=#pc^2hAHk2fL&@8WeJ(;{%_ zY)-IJ+mojcm)o0qiJO_{ut3)*0vh9rd}wBshLv?HY=4JbH%T`)Rast!t@84RSk@hO zA`}<$0j;{*e_McsJ2ho8L`a&wGf6Mulu1(sj^}cFehwsd68xPu-Uf^@*E#ewS#l*V zetx)(k)FS%^Pigei5mGj>-J9~Xl0B*k{gTh7ODSiUwT#P`|9ed)2iE}JkK4_Vh@K{ zq~VluTT3A|8Cu7?d_w&3=W}^FT`h7FNI3&@Scvc#>_~y^!)I=t_&>J`e~2(c%D4&9 zpCKiG;?DycXBradM;cEmF_>Aq0C?Ohy6chj5eOxCR{z-qIjO^)hStoT1zDiu%UIMVhmwqji`1=CNg~|FL zg(GHrm6_Q!STShw22?hY4x>Sv7n9)z{XrfkARzgjYtIb{$bX(g^t3KWf%Zly$@{cH zjxshwlewA6*uJyd$6_5X8P#K}-)fqN5&w#5OXB{!a28QTQ-JxbN8q#bpG$U-u4=r% zQm!e9{U2Ls3~Tb*Gk;%G-Wc)CBJgQ-Il^|O+Z7BGjN)KFFI