-
-
Save marguslt/a79ea204f99b45ab015b6ed1ff7529a4 to your computer and use it in GitHub Desktop.
mrg@TPXT:~/mc$ # http - https://httpie.org/ - a user-friendly command-line HTTP client for the API era | |
mrg@TPXT:~/mc$ # jq - https://stedolan.github.io/jq/ - a lightweight and flexible command-line JSON processor | |
mrg@TPXT:~/mc$ # starting with a clean profile, restored defaults watch values at movescount.com | |
mrg@TPXT:~/mc$ # load variables | |
mrg@TPXT:~/mc$ . mc_env.sh | |
mrg@TPXT:~/mc$ # encode Workout json | |
mrg@TPXT:~/mc$ jq -rc . 00-myworkout.json | base64 -w0 > 01-myworkout.base64 | |
mrg@TPXT:~/mc$ # replace Name and Source in rule template | |
mrg@TPXT:~/mc$ sed -e "s/RULE_NAME/My Workout/g" -e "s/RULE_SRC/$(<01-myworkout.base64)/g" < 10-rule_template.json > 02-myworkout_rule.json | |
mrg@TPXT:~/mc$ # list active Workouts (guidance type rules) | |
mrg@TPXT:~/mc$ http get https://uiservices.movescount.com/userdevices/$AMBIT_SN/rules type==guidance appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | jq ".[] | del(.Binary, .Source)" | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T19:02:04.3", | |
"Name": "uus", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644506, | |
"SelfURI": "rules/13644506", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T13:37:36.0", | |
"Name": "goobay", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644368, | |
"SelfURI": "rules/13644368", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
mrg@TPXT:~/mc$ # list all Workouts (guidance type rules) | |
mrg@TPXT:~/mc$ http get https://uiservices.movescount.com/rules/private type==guidance appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | jq ".[] | del(.Binary, .Source)" | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T20:29:25.9", | |
"Name": "k6igeuuem", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644540, | |
"SelfURI": "rules/13644540", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T19:02:04.3", | |
"Name": "uus", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644506, | |
"SelfURI": "rules/13644506", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T13:37:36.0", | |
"Name": "goobay", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644368, | |
"SelfURI": "rules/13644368", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
mrg@TPXT:~/mc$ # add new workout | |
mrg@TPXT:~/mc$ jq -cr . 02-myworkout_rule.json | http post https://uiservices.movescount.com/rules/ appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | |
HTTP/1.1 201 Created | |
Cache-Control: no-cache | |
Connection: keep-alive | |
Content-Length: 47 | |
Content-Type: application/json; charset=utf-8 | |
Date: Wed, 30 Sep 2020 19:15:57 GMT | |
Expires: -1 | |
Pragma: no-cache | |
Server: Microsoft-IIS/8.5 | |
Via: 1.1 f9fdb36a35b271b2043714f4c4655495.cloudfront.net (CloudFront) | |
X-Amz-Cf-Id: kPMYYmeCvnvHqK7gHRmkdUwvDiwEeTTyiw8AB3V3NM8Rp58Kc21TjQ== | |
X-Amz-Cf-Pop: ARN53 | |
X-AspNet-Version: 4.0.30319 | |
X-Cache: Miss from cloudfront | |
X-Powered-By: ASP.NET | |
{ | |
"RuleID": 13670761, | |
"RuleURI": "rules/13670761" | |
} | |
mrg@TPXT:~/mc$ # list all Workouts (guidance type rules), new rule should be added | |
mrg@TPXT:~/mc$ http get https://uiservices.movescount.com/rules/private type==guidance appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | jq ".[] | del(.Binary, .Source)" | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-09-30T19:15:56.4", | |
"Name": "My Workout", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13670761, | |
"SelfURI": "rules/13670761", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T20:29:25.9", | |
"Name": "k6igeuuem", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644540, | |
"SelfURI": "rules/13644540", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T19:02:04.3", | |
"Name": "uus", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644506, | |
"SelfURI": "rules/13644506", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T13:37:36.0", | |
"Name": "goobay", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644368, | |
"SelfURI": "rules/13644368", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
mrg@TPXT:~/mc$ # list active Workouts (guidance type rules), same - adding a rule does not change device settings | |
mrg@TPXT:~/mc$ http get https://uiservices.movescount.com/userdevices/$AMBIT_SN/rules type==guidance appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | jq ".[] | del(.Binary, .Source)" | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T19:02:04.3", | |
"Name": "uus", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644506, | |
"SelfURI": "rules/13644506", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-08-01T13:37:36.0", | |
"Name": "goobay", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13644368, | |
"SelfURI": "rules/13644368", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
mrg@TPXT:~/mc$ # replace old workout list with a new rule 13670761 | |
mrg@TPXT:~/mc$ # set watch S/N and rule list in settings template, pipe through httpie | |
mrg@TPXT:~/mc$ sed -e "s/AMBIT_SN/$AMBIT_SN/g" -e "s/RULE_ID_LIST/13670761/g" < 20-settings_template.json | jq -cr . | | |
> http put https://uiservices.movescount.com/userdevices/$AMBIT_SN/ resetchangedsettings==true appkey=="$APPKEY" userkey=="$USERKEY" email=="$ | |
MCUMAIL" User-Agent:"$MCUAGENT" | |
HTTP/1.1 200 OK | |
Cache-Control: no-cache | |
Connection: keep-alive | |
Content-Length: 0 | |
Date: Wed, 30 Sep 2020 19:38:23 GMT | |
Expires: -1 | |
Pragma: no-cache | |
Server: Microsoft-IIS/8.5 | |
Via: 1.1 482a78f386817c034ae15d18a92d3c98.cloudfront.net (CloudFront) | |
X-Amz-Cf-Id: vT95S1LInskSGKpFrEwaEMW62P0rwI7jST2n-Y7ELYvscs5tQETCIw== | |
X-Amz-Cf-Pop: ARN53 | |
X-AspNet-Version: 4.0.30319 | |
X-Cache: Miss from cloudfront | |
X-Powered-By: ASP.NET | |
mrg@TPXT:~/mc$ # list active Workouts (guidance type rules) | |
mrg@TPXT:~/mc$ http get https://uiservices.movescount.com/userdevices/$AMBIT_SN/rules type==guidance appkey=="$APPKEY" userkey=="$USERKEY" email=="$MCUMAIL" User-Agent:"$MCUAGENT" | jq ".[] | del(.Binary, .Source)" | |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"Description": null, | |
"IsPublic": false, | |
"LastModifiedDate": "2020-09-30T19:15:56.4", | |
"Name": "My Workout", | |
"OutputFormat": "onedecimal", | |
"Postfix": null, | |
"Prefix": null, | |
"RuleID": 13670761, | |
"SelfURI": "rules/13670761", | |
"TargetVirtualMachineVersion": "0.08, build 15.8.18.0", | |
"Type": "guidance" | |
} | |
mrg@TPXT:~/mc$ |
{ | |
"name": "My Workout", | |
"steps": [ | |
{ | |
"duration": { | |
"durationName": "time", | |
"value": 600 | |
}, | |
"target": { | |
"targetName": "hr", | |
"valueRange": { | |
"max": 2.1666666666666665, | |
"min": 2 | |
} | |
}, | |
"text": "Easy jogging", | |
"type": { | |
"typeName": "warmup" | |
} | |
}, | |
{ | |
"type": { | |
"typeName": "repeatStart", | |
"value": 3 | |
} | |
}, | |
{ | |
"duration": { | |
"durationName": "distance", | |
"value": 1000 | |
}, | |
"target": { | |
"targetName": "pace", | |
"valueRange": { | |
"max": 0.25, | |
"min": 0.27 | |
} | |
}, | |
"text": "Run fast for 1km", | |
"type": { | |
"typeName": "interval" | |
} | |
}, | |
{ | |
"duration": { | |
"durationName": "hr_below", | |
"value": 2.1666666666666665 | |
}, | |
"target": { | |
"targetName": "none", | |
"valueRange": { | |
"max": 0, | |
"min": 0 | |
} | |
}, | |
"text": "Walk to recover", | |
"type": { | |
"typeName": "recovery" | |
} | |
}, | |
{ | |
"type": { | |
"typeName": "repeatEnd" | |
} | |
}, | |
{ | |
"duration": { | |
"durationName": "time", | |
"value": 600 | |
}, | |
"target": { | |
"targetName": "hr", | |
"valueRange": { | |
"max": 1.8333333333333333, | |
"min": 1.3333333333333333 | |
} | |
}, | |
"text": "Walk to cool down", | |
"type": { | |
"typeName": "cooldown" | |
} | |
} | |
], | |
"workoutDescription": "Basic running workout" | |
} |
eyJuYW1lIjoiTXkgV29ya291dCIsInN0ZXBzIjpbeyJkdXJhdGlvbiI6eyJkdXJhdGlvbk5hbWUiOiJ0aW1lIiwidmFsdWUiOjYwMH0sInRhcmdldCI6eyJ0YXJnZXROYW1lIjoiaHIiLCJ2YWx1ZVJhbmdlIjp7Im1heCI6Mi4xNjY2NjY2NjY2NjY2NjY1LCJtaW4iOjJ9fSwidGV4dCI6IkVhc3kgam9nZ2luZyIsInR5cGUiOnsidHlwZU5hbWUiOiJ3YXJtdXAifX0seyJ0eXBlIjp7InR5cGVOYW1lIjoicmVwZWF0U3RhcnQiLCJ2YWx1ZSI6M319LHsiZHVyYXRpb24iOnsiZHVyYXRpb25OYW1lIjoiZGlzdGFuY2UiLCJ2YWx1ZSI6MTAwMH0sInRhcmdldCI6eyJ0YXJnZXROYW1lIjoicGFjZSIsInZhbHVlUmFuZ2UiOnsibWF4IjowLjI1LCJtaW4iOjAuMjd9fSwidGV4dCI6IlJ1biBmYXN0IGZvciAxa20iLCJ0eXBlIjp7InR5cGVOYW1lIjoiaW50ZXJ2YWwifX0seyJkdXJhdGlvbiI6eyJkdXJhdGlvbk5hbWUiOiJocl9iZWxvdyIsInZhbHVlIjoyLjE2NjY2NjY2NjY2NjY2NjV9LCJ0YXJnZXQiOnsidGFyZ2V0TmFtZSI6Im5vbmUiLCJ2YWx1ZVJhbmdlIjp7Im1heCI6MCwibWluIjowfX0sInRleHQiOiJXYWxrIHRvIHJlY292ZXIiLCJ0eXBlIjp7InR5cGVOYW1lIjoicmVjb3ZlcnkifX0seyJ0eXBlIjp7InR5cGVOYW1lIjoicmVwZWF0RW5kIn19LHsiZHVyYXRpb24iOnsiZHVyYXRpb25OYW1lIjoidGltZSIsInZhbHVlIjo2MDB9LCJ0YXJnZXQiOnsidGFyZ2V0TmFtZSI6ImhyIiwidmFsdWVSYW5nZSI6eyJtYXgiOjEuODMzMzMzMzMzMzMzMzMzMywibWluIjoxLjMzMzMzMzMzMzMzMzMzMzN9fSwidGV4dCI6IldhbGsgdG8gY29vbCBkb3duIiwidHlwZSI6eyJ0eXBlTmFtZSI6ImNvb2xkb3duIn19XSwid29ya291dERlc2NyaXB0aW9uIjoiQmFzaWMgcnVubmluZyB3b3Jrb3V0In0K |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"IsPublic": false, | |
"Name": "My Workout", | |
"OutputFormat": "onedecimal", | |
"Source": "eyJuYW1lIjoiTXkgV29ya291dCIsInN0ZXBzIjpbeyJkdXJhdGlvbiI6eyJkdXJhdGlvbk5hbWUiOiJ0aW1lIiwidmFsdWUiOjYwMH0sInRhcmdldCI6eyJ0YXJnZXROYW1lIjoiaHIiLCJ2YWx1ZVJhbmdlIjp7Im1heCI6Mi4xNjY2NjY2NjY2NjY2NjY1LCJtaW4iOjJ9fSwidGV4dCI6IkVhc3kgam9nZ2luZyIsInR5cGUiOnsidHlwZU5hbWUiOiJ3YXJtdXAifX0seyJ0eXBlIjp7InR5cGVOYW1lIjoicmVwZWF0U3RhcnQiLCJ2YWx1ZSI6M319LHsiZHVyYXRpb24iOnsiZHVyYXRpb25OYW1lIjoiZGlzdGFuY2UiLCJ2YWx1ZSI6MTAwMH0sInRhcmdldCI6eyJ0YXJnZXROYW1lIjoicGFjZSIsInZhbHVlUmFuZ2UiOnsibWF4IjowLjI1LCJtaW4iOjAuMjd9fSwidGV4dCI6IlJ1biBmYXN0IGZvciAxa20iLCJ0eXBlIjp7InR5cGVOYW1lIjoiaW50ZXJ2YWwifX0seyJkdXJhdGlvbiI6eyJkdXJhdGlvbk5hbWUiOiJocl9iZWxvdyIsInZhbHVlIjoyLjE2NjY2NjY2NjY2NjY2NjV9LCJ0YXJnZXQiOnsidGFyZ2V0TmFtZSI6Im5vbmUiLCJ2YWx1ZVJhbmdlIjp7Im1heCI6MCwibWluIjowfX0sInRleHQiOiJXYWxrIHRvIHJlY292ZXIiLCJ0eXBlIjp7InR5cGVOYW1lIjoicmVjb3ZlcnkifX0seyJ0eXBlIjp7InR5cGVOYW1lIjoicmVwZWF0RW5kIn19LHsiZHVyYXRpb24iOnsiZHVyYXRpb25OYW1lIjoidGltZSIsInZhbHVlIjo2MDB9LCJ0YXJnZXQiOnsidGFyZ2V0TmFtZSI6ImhyIiwidmFsdWVSYW5nZSI6eyJtYXgiOjEuODMzMzMzMzMzMzMzMzMzMywibWluIjoxLjMzMzMzMzMzMzMzMzMzMzN9fSwidGV4dCI6IldhbGsgdG8gY29vbCBkb3duIiwidHlwZSI6eyJ0eXBlTmFtZSI6ImNvb2xkb3duIn19XSwid29ya291dERlc2NyaXB0aW9uIjoiQmFzaWMgcnVubmluZyB3b3Jrb3V0In0K", | |
"Type": "guidance" | |
} |
{ | |
"ActivityID": 1, | |
"Category": "guidance", | |
"IsPublic": false, | |
"Name": "RULE_NAME", | |
"OutputFormat": "onedecimal", | |
"Source": "RULE_SRC", | |
"Type": "guidance" | |
} |
{ | |
"DeviceName": "Emu", | |
"FirmwareVersion": "2.4.17", | |
"HardwareVersion": "69.4.17414", | |
"SerialNumber": "AMBIT_SN", | |
"Settings": { | |
"RuleIDs": [ | |
RULE_ID_LIST | |
] | |
} | |
} |
#Watch Serial number: | |
AMBIT_SN=... | |
#AppKey (for Suuntolink: %LocalAppData%\Suuntolink\app-3.1.4\resources\app\production.json ) | |
APPKEY=... | |
#UserKey (for Suuntolink: %AppData%\Suuntolink\suuntolink_data.json ) | |
USERKEY=... | |
#Mail used with MC account | |
MCMAIL=... | |
#User-Agent used for MC queries: | |
MCUAGENT="Komposti/2.29.0 (Android=5.1.1) ArREST/1.0 libcurl/7.47.0" |
Hey I just created few very simple scripts to upload and download workouts following your steps.
Check here: https://gist.github.com/dgvalde/4bb9a9dc2162c27440a978b217c01b7e
Many thanks!
P.D: I notice that you have a typo on the http calls. You have $MCUMAIL instead of $MCMAIL. Anyhow, it seems it still works without email.
@dgvalde 👍
Though a word of warning :/ - the way I'm updating settings here ( setting only RuleIDs
array ) might have a slight negative effect on your Movescount settings. At least I managed to clear all my deactivated sport modes somehow, and its probably due this partial Settings
JSON or resetchangedsettings
request parameter. Sending back complete Settings
JSON is probably a safer option. And creating a separate Movescount account for those experiments can't hurt.
I haven't built anything around those bits yet, it was more of an attempt to explore and document Workout handling before Movescount app access to MC service was closed. And as some folks at forums.suunto.com were interested, I had a reason to give it a bit of structure.
Though I toyed around with few ideas, like implementing a Workout builder as Chrome extension so it would extend Movescount.com; or as a patch to Suuntolink (it's Electron app, should be doable), so it would be kind of integrated into sync tool; or perhaps just integrate it into Openambit. But as I don't feel quite at home with any of those options, it would take a lot more time and effort I'm willing to spare right now.
Oh, and thanks for catching that typo 🙃
BTW, when exploring Movescount Android APK a bit (e.g. wirh jadx), you'll find all the bits to build those Workout JSONs from scratch.
Wow, this is so amazing, you just made my life easier! many thanks for this feature.
Are these steps arelady part of any application to use them more easily?
Do you plan to integrate this somewhere?
The best thing is that while on the Smartphone app it only lets you set power steps in hundreds (e.g. 200 - 300) on the file I can set any power target (e.g. 262 - 287) 👍
Many thanks