Compare commits

..

13 Commits

Author SHA1 Message Date
cf6552410e Restyled by whitespace 2024-01-27 15:39:45 +00:00
6b3a17bea3 Add GPS accuracy to update_location webhook and changed activity reporting 2024-01-27 15:39:22 +00:00
8f372c03e3 Merge branch 'main' into 94-add-gps-reporting-to-the-background-service 2024-01-27 15:06:06 +00:00
f5f88ced4e Added web-based editor to documentation 2024-01-27 14:59:48 +00:00
96abff9339 Fix spacing of arrows 2024-01-27 14:02:05 +00:00
4ff1509046 API level fix
Added 'has' test for Activity.getProfileInfo(). Reverted automatic changes to XML prettiness.

Co-Authored-By: Joseph Abbey <me@josephabbey.dev>
2024-01-27 13:57:21 +00:00
1ec80a1704 Update restyled.yml 2024-01-27 13:39:44 +00:00
e3288c9353 GPS and current activity in background service 2024-01-27 13:11:24 +00:00
2ba102f8dd Merge pull request #92 from house-of-abbey/91-enhanced-web-editor 2024-01-25 19:56:40 +00:00
3e0789e808 Update config.schema.json 2024-01-25 19:34:51 +00:00
bd56c6b4e6 Titles for buttons 2024-01-25 15:19:39 +00:00
6063ae8ba3 Titles for form elements 2024-01-25 14:56:28 +00:00
f00bbdcb13 Merge pull request #93 from house-of-abbey/restyled/91-enhanced-web-editor 2024-01-25 14:30:59 +00:00
9 changed files with 429 additions and 270 deletions

View File

@ -1,3 +1,5 @@
exclude:
- '**/*.md'
- '**/pnpm-lock.yaml'
- 'manifest.xml'
- 'manifest-widget.xml'

View File

@ -184,9 +184,10 @@ This allows the `confirm` field to be accommodated in the `tap_action` along sid
## Editing the JSON file
You have options. The first is what we use.
1. **Best!** Use the [Studio Code Server](https://community.home-assistant.io/t/home-assistant-community-add-on-visual-studio-code/107863) addon for Home Assistant. You can then edit your JSON file in place.
2. Locally installed VSCode, or if not installed,
3. try the on-line version at https://vscode.dev/, which works really well.
1. **Best!** Use the GarminHomeAssistant [Web-based Editor](https://house-of-abbey.github.io/GarminHomeAssistant/web/) which includes `entity` and `service` name completion and validation by fetching data from your own Home Assistant instance. _Pretty nifty eh?_ The other method listed below do not add this convenience and checking.
2. Use the [Studio Code Server](https://community.home-assistant.io/t/home-assistant-community-add-on-visual-studio-code/107863) addon for Home Assistant. You can then edit your JSON file in place.
3. Locally installed VSCode, or if not installed, try
4. The on-line version at https://vscode.dev/, which works really well.
Paste in your JSON (and change the file type to JSON if not saving), it will then verify your file format and schema for you, highlighting any errors for you to fix.

View File

@ -2,6 +2,19 @@
# Troubleshooting Guides
## Check your JSON Schema
Before [raising an issue](https://github.com/house-of-abbey/GarminHomeAssistant/issues) about a possible bug, _please, please_ check your JSON is compliant with both the JSON format and our schema. To do this you have options. The first is what we use.
1. **Best!** Use the GarminHomeAssistant [Web-based Editor](https://house-of-abbey.github.io/GarminHomeAssistant/web/) which includes `entity` and `service` name completion and validation by fetching data from your own Home Assistant instance. _Pretty nifty eh?_ The other method listed below do not add this convenience and checking.
2. Use the [Studio Code Server](https://community.home-assistant.io/t/home-assistant-community-add-on-visual-studio-code/107863) addon for Home Assistant. You can then edit your JSON file in place.
3. Locally installed VSCode, or if not installed, try
4. The on-line version at https://vscode.dev/, which works really well. Paste in your JSON (and change the file type to JSON if not saving), it will then verify your file format and schema for you, highlighting any errors for you to fix.
A failure to get the file format right tends to mean that the response to the application errors with `INVALID_HTTP_BODY_IN_NETWORK_RESPONSE` (code of -400). This means the response did not contain JSON, it was probably an error message in plain text that could not be parsed by the Connect IQ API call. See [Toybox.Communications](https://developer.garmin.com/connect-iq/api-docs/Toybox/Communications.html) for the list of error code you might be presented with on your device.
Make sure you can browse to the URL of your JSON file in a standard web browser to make sure it is accessible.
## Watch Menu and API
With either of the following setups, there are inevitably some problems along the way. GarminHomeAssistant is careful to rely only on having working URLs. Getting them working is the user's responsibility. However, we have developed some fault finding tools.

View File

@ -44,30 +44,60 @@
"additionalProperties": false
},
"template": {
"type": "object",
"properties": {
"entity": {
"$ref": "#/$defs/entity"
"oneOf": [
{
"type": "object",
"properties": {
"entity": {
"$ref": "#/$defs/entity",
"deprecated": true,
"title": "Schema change:",
"description": "Use 'tap_action' instead to mirror Home Assistant."
},
"name": {
"title": "Your familiar name",
"type": "string"
},
"content": {
"title": "What to display (template)",
"type": "string"
},
"type": {
"title": "Menu item type",
"description": "One of 'tap', 'template', 'toggle' or 'group'.",
"const": "template"
}
},
"required": ["name", "content", "type"],
"additionalProperties": false
},
"name": {
"title": "Your familiar name",
"type": "string"
},
"content": {
"title": "What to display (template)",
"type": "string"
},
"type": {
"title": "Menu item type",
"description": "One of 'tap', 'template', 'toggle' or 'group'.",
"const": "template"
},
"tap_action": {
"$ref": "#/$defs/tap_action"
{
"type": "object",
"properties": {
"entity": {
"$ref": "#/$defs/entity"
},
"name": {
"title": "Your familiar name",
"type": "string"
},
"content": {
"title": "What to display (template)",
"type": "string"
},
"type": {
"title": "Menu item type",
"description": "One of 'tap', 'template', 'toggle' or 'group'.",
"const": "template"
},
"tap_action": {
"$ref": "#/$defs/tap_action"
}
},
"required": ["name", "content", "type", "tap_action"],
"additionalProperties": false
}
},
"required": ["name", "content", "type"],
"additionalProperties": false
]
},
"tap": {
"type": "object",
@ -110,7 +140,6 @@
"entity": {
"$ref": "#/$defs/entity",
"type": "string",
"pattern": "^[^.]+\\.[^.]+$",
"deprecated": true,
"title": "Schema change:",
"description": "'entity' is no longer necessary and should now be removed."

View File

@ -34,115 +34,116 @@
"Monkey C: Edit Products" - Lets you add or remove any product
-->
<iq:products>
<iq:product id="approachs7042mm"/>
<iq:product id="approachs7047mm"/>
<iq:product id="d2air"/>
<iq:product id="d2airx10"/>
<iq:product id="d2delta"/>
<iq:product id="d2deltapx"/>
<iq:product id="d2deltas"/>
<iq:product id="d2mach1"/>
<iq:product id="descentg1"/>
<iq:product id="descentmk2"/>
<iq:product id="descentmk2s"/>
<iq:product id="edge1030"/>
<iq:product id="edge1030bontrager"/>
<iq:product id="edge1030plus"/>
<iq:product id="edge1040"/>
<iq:product id="edge520plus"/>
<iq:product id="edge530"/>
<iq:product id="edge820"/>
<iq:product id="edge830"/>
<iq:product id="edgeexplore"/>
<iq:product id="edgeexplore2"/>
<iq:product id="enduro"/>
<iq:product id="epix2"/>
<iq:product id="epix2pro42mm"/>
<iq:product id="epix2pro47mm"/>
<iq:product id="epix2pro51mm"/>
<iq:product id="fenix5"/>
<iq:product id="fenix5plus"/>
<iq:product id="fenix5s"/>
<iq:product id="fenix5splus"/>
<iq:product id="fenix5x"/>
<iq:product id="fenix5xplus"/>
<iq:product id="fenix6"/>
<iq:product id="fenix6pro"/>
<iq:product id="fenix6s"/>
<iq:product id="fenix6spro"/>
<iq:product id="fenix6xpro"/>
<iq:product id="fenix7"/>
<iq:product id="fenix7pro"/>
<iq:product id="fenix7pronowifi"/>
<iq:product id="fenix7s"/>
<iq:product id="fenix7spro"/>
<iq:product id="fenix7x"/>
<iq:product id="fenix7xpro"/>
<iq:product id="fenix7xpronowifi"/>
<iq:product id="fenixchronos"/>
<iq:product id="fr245"/>
<iq:product id="fr245m"/>
<iq:product id="fr255"/>
<iq:product id="fr255m"/>
<iq:product id="fr255s"/>
<iq:product id="fr255sm"/>
<iq:product id="fr265"/>
<iq:product id="fr265s"/>
<iq:product id="fr55"/>
<iq:product id="fr645"/>
<iq:product id="fr645m"/>
<iq:product id="fr745"/>
<iq:product id="fr935"/>
<iq:product id="fr945"/>
<iq:product id="fr945lte"/>
<iq:product id="fr955"/>
<iq:product id="fr965"/>
<iq:product id="gpsmap67"/>
<iq:product id="instinct2"/>
<iq:product id="instinct2s"/>
<iq:product id="instinct2x"/>
<iq:product id="instinctcrossover"/>
<iq:product id="legacyherocaptainmarvel"/>
<iq:product id="legacyherofirstavenger"/>
<iq:product id="legacysagadarthvader"/>
<iq:product id="legacysagarey"/>
<iq:product id="marq2"/>
<iq:product id="marq2aviator"/>
<iq:product id="marqadventurer"/>
<iq:product id="marqathlete"/>
<iq:product id="marqaviator"/>
<iq:product id="marqcaptain"/>
<iq:product id="marqcommander"/>
<iq:product id="marqdriver"/>
<iq:product id="marqexpedition"/>
<iq:product id="marqgolfer"/>
<iq:product id="montana7xx"/>
<iq:product id="venu"/>
<iq:product id="venu2"/>
<iq:product id="venu2plus"/>
<iq:product id="venu2s"/>
<iq:product id="venu3"/>
<iq:product id="venu3s"/>
<iq:product id="venud"/>
<iq:product id="venusq"/>
<iq:product id="venusq2"/>
<iq:product id="venusq2m"/>
<iq:product id="venusqm"/>
<iq:product id="vivoactive3"/>
<iq:product id="vivoactive3m"/>
<iq:product id="vivoactive3mlte"/>
<iq:product id="vivoactive4"/>
<iq:product id="vivoactive4s"/>
<iq:product id="vivoactive5"/>
<iq:product id="approachs7042mm" />
<iq:product id="approachs7047mm" />
<iq:product id="d2air" />
<iq:product id="d2airx10" />
<iq:product id="d2delta" />
<iq:product id="d2deltapx" />
<iq:product id="d2deltas" />
<iq:product id="d2mach1" />
<iq:product id="descentg1" />
<iq:product id="descentmk2" />
<iq:product id="descentmk2s" />
<iq:product id="edge1030" />
<iq:product id="edge1030bontrager" />
<iq:product id="edge1030plus" />
<iq:product id="edge1040" />
<iq:product id="edge520plus" />
<iq:product id="edge530" />
<iq:product id="edge820" />
<iq:product id="edge830" />
<iq:product id="edgeexplore" />
<iq:product id="edgeexplore2" />
<iq:product id="enduro" />
<iq:product id="epix2" />
<iq:product id="epix2pro42mm" />
<iq:product id="epix2pro47mm" />
<iq:product id="epix2pro51mm" />
<iq:product id="fenix5" />
<iq:product id="fenix5plus" />
<iq:product id="fenix5s" />
<iq:product id="fenix5splus" />
<iq:product id="fenix5x" />
<iq:product id="fenix5xplus" />
<iq:product id="fenix6" />
<iq:product id="fenix6pro" />
<iq:product id="fenix6s" />
<iq:product id="fenix6spro" />
<iq:product id="fenix6xpro" />
<iq:product id="fenix7" />
<iq:product id="fenix7pro" />
<iq:product id="fenix7pronowifi" />
<iq:product id="fenix7s" />
<iq:product id="fenix7spro" />
<iq:product id="fenix7x" />
<iq:product id="fenix7xpro" />
<iq:product id="fenix7xpronowifi" />
<iq:product id="fenixchronos" />
<iq:product id="fr245" />
<iq:product id="fr245m" />
<iq:product id="fr255" />
<iq:product id="fr255m" />
<iq:product id="fr255s" />
<iq:product id="fr255sm" />
<iq:product id="fr265" />
<iq:product id="fr265s" />
<iq:product id="fr55" />
<iq:product id="fr645" />
<iq:product id="fr645m" />
<iq:product id="fr745" />
<iq:product id="fr935" />
<iq:product id="fr945" />
<iq:product id="fr945lte" />
<iq:product id="fr955" />
<iq:product id="fr965" />
<iq:product id="gpsmap67" />
<iq:product id="instinct2" />
<iq:product id="instinct2s" />
<iq:product id="instinct2x" />
<iq:product id="instinctcrossover" />
<iq:product id="legacyherocaptainmarvel" />
<iq:product id="legacyherofirstavenger" />
<iq:product id="legacysagadarthvader" />
<iq:product id="legacysagarey" />
<iq:product id="marq2" />
<iq:product id="marq2aviator" />
<iq:product id="marqadventurer" />
<iq:product id="marqathlete" />
<iq:product id="marqaviator" />
<iq:product id="marqcaptain" />
<iq:product id="marqcommander" />
<iq:product id="marqdriver" />
<iq:product id="marqexpedition" />
<iq:product id="marqgolfer" />
<iq:product id="montana7xx" />
<iq:product id="venu" />
<iq:product id="venu2" />
<iq:product id="venu2plus" />
<iq:product id="venu2s" />
<iq:product id="venu3" />
<iq:product id="venu3s" />
<iq:product id="venud" />
<iq:product id="venusq" />
<iq:product id="venusq2" />
<iq:product id="venusq2m" />
<iq:product id="venusqm" />
<iq:product id="vivoactive3" />
<iq:product id="vivoactive3m" />
<iq:product id="vivoactive3mlte" />
<iq:product id="vivoactive4" />
<iq:product id="vivoactive4s" />
<iq:product id="vivoactive5" />
</iq:products>
<!--
Use "Monkey C: Edit Permissions" from the Visual Studio Code command
palette to update permissions.
-->
<iq:permissions>
<iq:uses-permission id="Background"/>
<iq:uses-permission id="BluetoothLowEnergy"/>
<iq:uses-permission id="Communications"/>
<iq:uses-permission id="Background" />
<iq:uses-permission id="BluetoothLowEnergy" />
<iq:uses-permission id="Communications" />
<iq:uses-permission id="Positioning" />
</iq:permissions>
<!--
Use "Monkey C: Edit Languages" from the Visual Studio Code command
@ -190,6 +191,6 @@
Use "Monkey C: Configure Monkey Barrel" from the Visual Studio Code
command palette to edit the included barrels.
-->
<iq:barrels/>
<iq:barrels />
</iq:application>
</iq:manifest>

View File

@ -40,115 +40,116 @@
"Monkey C: Edit Products" - Lets you add or remove any product
-->
<iq:products>
<iq:product id="approachs7042mm"/>
<iq:product id="approachs7047mm"/>
<iq:product id="d2air"/>
<iq:product id="d2airx10"/>
<iq:product id="d2delta"/>
<iq:product id="d2deltapx"/>
<iq:product id="d2deltas"/>
<iq:product id="d2mach1"/>
<iq:product id="descentg1"/>
<iq:product id="descentmk2"/>
<iq:product id="descentmk2s"/>
<iq:product id="edge1030"/>
<iq:product id="edge1030bontrager"/>
<iq:product id="edge1030plus"/>
<iq:product id="edge1040"/>
<iq:product id="edge520plus"/>
<iq:product id="edge530"/>
<iq:product id="edge820"/>
<iq:product id="edge830"/>
<iq:product id="edgeexplore"/>
<iq:product id="edgeexplore2"/>
<iq:product id="enduro"/>
<iq:product id="epix2"/>
<iq:product id="epix2pro42mm"/>
<iq:product id="epix2pro47mm"/>
<iq:product id="epix2pro51mm"/>
<iq:product id="fenix5"/>
<iq:product id="fenix5plus"/>
<iq:product id="fenix5s"/>
<iq:product id="fenix5splus"/>
<iq:product id="fenix5x"/>
<iq:product id="fenix5xplus"/>
<iq:product id="fenix6"/>
<iq:product id="fenix6pro"/>
<iq:product id="fenix6s"/>
<iq:product id="fenix6spro"/>
<iq:product id="fenix6xpro"/>
<iq:product id="fenix7"/>
<iq:product id="fenix7pro"/>
<iq:product id="fenix7pronowifi"/>
<iq:product id="fenix7s"/>
<iq:product id="fenix7spro"/>
<iq:product id="fenix7x"/>
<iq:product id="fenix7xpro"/>
<iq:product id="fenix7xpronowifi"/>
<iq:product id="fenixchronos"/>
<iq:product id="fr245"/>
<iq:product id="fr245m"/>
<iq:product id="fr255"/>
<iq:product id="fr255m"/>
<iq:product id="fr255s"/>
<iq:product id="fr255sm"/>
<iq:product id="fr265"/>
<iq:product id="fr265s"/>
<iq:product id="fr55"/>
<iq:product id="fr645"/>
<iq:product id="fr645m"/>
<iq:product id="fr745"/>
<iq:product id="fr935"/>
<iq:product id="fr945"/>
<iq:product id="fr945lte"/>
<iq:product id="fr955"/>
<iq:product id="fr965"/>
<iq:product id="gpsmap67"/>
<iq:product id="instinct2"/>
<iq:product id="instinct2s"/>
<iq:product id="instinct2x"/>
<iq:product id="instinctcrossover"/>
<iq:product id="legacyherocaptainmarvel"/>
<iq:product id="legacyherofirstavenger"/>
<iq:product id="legacysagadarthvader"/>
<iq:product id="legacysagarey"/>
<iq:product id="marq2"/>
<iq:product id="marq2aviator"/>
<iq:product id="marqadventurer"/>
<iq:product id="marqathlete"/>
<iq:product id="marqaviator"/>
<iq:product id="marqcaptain"/>
<iq:product id="marqcommander"/>
<iq:product id="marqdriver"/>
<iq:product id="marqexpedition"/>
<iq:product id="marqgolfer"/>
<iq:product id="montana7xx"/>
<iq:product id="venu"/>
<iq:product id="venu2"/>
<iq:product id="venu2plus"/>
<iq:product id="venu2s"/>
<iq:product id="venu3"/>
<iq:product id="venu3s"/>
<iq:product id="venud"/>
<iq:product id="venusq"/>
<iq:product id="venusq2"/>
<iq:product id="venusq2m"/>
<iq:product id="venusqm"/>
<iq:product id="vivoactive3"/>
<iq:product id="vivoactive3m"/>
<iq:product id="vivoactive3mlte"/>
<iq:product id="vivoactive4"/>
<iq:product id="vivoactive4s"/>
<iq:product id="vivoactive5"/>
<iq:product id="approachs7042mm" />
<iq:product id="approachs7047mm" />
<iq:product id="d2air" />
<iq:product id="d2airx10" />
<iq:product id="d2delta" />
<iq:product id="d2deltapx" />
<iq:product id="d2deltas" />
<iq:product id="d2mach1" />
<iq:product id="descentg1" />
<iq:product id="descentmk2" />
<iq:product id="descentmk2s" />
<iq:product id="edge1030" />
<iq:product id="edge1030bontrager" />
<iq:product id="edge1030plus" />
<iq:product id="edge1040" />
<iq:product id="edge520plus" />
<iq:product id="edge530" />
<iq:product id="edge820" />
<iq:product id="edge830" />
<iq:product id="edgeexplore" />
<iq:product id="edgeexplore2" />
<iq:product id="enduro" />
<iq:product id="epix2" />
<iq:product id="epix2pro42mm" />
<iq:product id="epix2pro47mm" />
<iq:product id="epix2pro51mm" />
<iq:product id="fenix5" />
<iq:product id="fenix5plus" />
<iq:product id="fenix5s" />
<iq:product id="fenix5splus" />
<iq:product id="fenix5x" />
<iq:product id="fenix5xplus" />
<iq:product id="fenix6" />
<iq:product id="fenix6pro" />
<iq:product id="fenix6s" />
<iq:product id="fenix6spro" />
<iq:product id="fenix6xpro" />
<iq:product id="fenix7" />
<iq:product id="fenix7pro" />
<iq:product id="fenix7pronowifi" />
<iq:product id="fenix7s" />
<iq:product id="fenix7spro" />
<iq:product id="fenix7x" />
<iq:product id="fenix7xpro" />
<iq:product id="fenix7xpronowifi" />
<iq:product id="fenixchronos" />
<iq:product id="fr245" />
<iq:product id="fr245m" />
<iq:product id="fr255" />
<iq:product id="fr255m" />
<iq:product id="fr255s" />
<iq:product id="fr255sm" />
<iq:product id="fr265" />
<iq:product id="fr265s" />
<iq:product id="fr55" />
<iq:product id="fr645" />
<iq:product id="fr645m" />
<iq:product id="fr745" />
<iq:product id="fr935" />
<iq:product id="fr945" />
<iq:product id="fr945lte" />
<iq:product id="fr955" />
<iq:product id="fr965" />
<iq:product id="gpsmap67" />
<iq:product id="instinct2" />
<iq:product id="instinct2s" />
<iq:product id="instinct2x" />
<iq:product id="instinctcrossover" />
<iq:product id="legacyherocaptainmarvel" />
<iq:product id="legacyherofirstavenger" />
<iq:product id="legacysagadarthvader" />
<iq:product id="legacysagarey" />
<iq:product id="marq2" />
<iq:product id="marq2aviator" />
<iq:product id="marqadventurer" />
<iq:product id="marqathlete" />
<iq:product id="marqaviator" />
<iq:product id="marqcaptain" />
<iq:product id="marqcommander" />
<iq:product id="marqdriver" />
<iq:product id="marqexpedition" />
<iq:product id="marqgolfer" />
<iq:product id="montana7xx" />
<iq:product id="venu" />
<iq:product id="venu2" />
<iq:product id="venu2plus" />
<iq:product id="venu2s" />
<iq:product id="venu3" />
<iq:product id="venu3s" />
<iq:product id="venud" />
<iq:product id="venusq" />
<iq:product id="venusq2" />
<iq:product id="venusq2m" />
<iq:product id="venusqm" />
<iq:product id="vivoactive3" />
<iq:product id="vivoactive3m" />
<iq:product id="vivoactive3mlte" />
<iq:product id="vivoactive4" />
<iq:product id="vivoactive4s" />
<iq:product id="vivoactive5" />
</iq:products>
<!--
Use "Monkey C: Edit Permissions" from the Visual Studio Code command
palette to update permissions.
-->
<iq:permissions>
<iq:uses-permission id="Background"/>
<iq:uses-permission id="BluetoothLowEnergy"/>
<iq:uses-permission id="Communications"/>
<iq:uses-permission id="Background" />
<iq:uses-permission id="BluetoothLowEnergy" />
<iq:uses-permission id="Communications" />
<iq:uses-permission id="Positioning" />
</iq:permissions>
<!--
Use "Monkey C: Edit Languages" from the Visual Studio Code command
@ -196,6 +197,6 @@
Use "Monkey C: Configure Monkey Barrel" from the Visual Studio Code
command palette to edit the included barrels.
-->
<iq:barrels/>
<iq:barrels />
</iq:application>
</iq:manifest>

View File

@ -38,28 +38,87 @@ 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
if (position.accuracy != Position.QUALITY_NOT_AVAILABLE && position.accuracy != Position.QUALITY_LAST_KNOWN) {
var accuracy = 0;
switch (position.accuracy) {
case Position.QUALITY_POOR:
accuracy = 500;
break;
case Position.QUALITY_USABLE:
accuracy = 100;
break;
case Position.QUALITY_GOOD:
accuracy = 10;
break;
}
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" => accuracy,
"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)
);
}
var data = [
{
"state" => System.getSystemStats().battery,
"type" => "sensor",
"unique_id" => "battery_level"
},
{
"state" => System.getSystemStats().charging,
"type" => "binary_sensor",
"unique_id" => "battery_is_charging"
}
];
if (Activity has :getProfileInfo) {
data.add({
"state" => Activity.getProfileInfo().sport,
"type" => "sensor",
"unique_id" => "activity"
});
data.add({
"state" => Activity.getProfileInfo().subSport,
"type" => "sensor",
"unique_id" => "sub_activity"
});
}
Communications.makeWebRequest(
(Properties.getValue("api_url") as Lang.String) + "/webhook/" + (Properties.getValue("webhook_id") as Lang.String),
{
"type" => "update_sensor_states",
"data" => [
{
"state" => System.getSystemStats().battery,
"type" => "sensor",
"unique_id" => "battery_level"
},
{
"state" => System.getSystemStats().charging,
"type" => "binary_sensor",
"unique_id" => "battery_is_charging"
}
]
"data" => data
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,

View File

@ -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,51 @@ 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");
if (Activity has :getProfileInfo) {
registerWebhookSensor({
"name" => "Activity",
"state" => Activity.getProfileInfo().sport,
"type" => "sensor",
"unique_id" => "activity",
"disabled" => false
}, 2);
break;
}
case 2:
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Registering next sensor: Activity");
if (Activity has :getProfileInfo) {
registerWebhookSensor({
"name" => "Sub-activity",
"state" => Activity.getProfileInfo().subSport,
"type" => "sensor",
"unique_id" => "sub_activity",
"disabled" => false
}, 3);
break;
}
default:
}
} else {
// System.println("WebhookManager onReturnRegisterWebhookSensor(): Failure");
Settings.unsetWebhookId();
Settings.unsetIsBatteryLevelEnabled();
ErrorView.show(WatchUi.loadResource($.Rez.Strings.WebhookFailed) as Lang.String);
@ -189,7 +222,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 +235,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)
);

View File

@ -221,21 +221,29 @@
<input
required
placeholder="https://<home-assistant>/api"
title="Home Assistant API URL `https://<home-assistant>/api`"
type="url"
name="api_url"
id="api_url"
pattern="https://.*/api" />
<input
placeholder="https://<home-assistant>/local/garmin/menu.json"
title="Menu JSON URL `https://<home-assistant>/local/garmin/menu.json`"
type="url"
name="menu_url"
id="menu_url"
pattern="https://.*\.json" />
<button class="icon" icon="download" id="download" type="button"></button>
<button
title="Download content of menu url and put it in the editor"
class="icon"
icon="download"
id="download"
type="button"></button>
<input
required
autocomplete="new-password"
placeholder="token"
placeholder="API Token"
title="Home Assistant Long-lived Access Token"
type="password"
name="api_token"
id="api_token" />
@ -249,6 +257,7 @@
<div class="row">
<h2>GarminHomeAssistant Troubleshooting</h2>
<button
title="Close"
class="icon"
icon="close"
onclick="this.parentElement.parentElement.parentElement.close()"
@ -262,11 +271,21 @@
<div class="row">
<div id="test-api-response">Check now!</div>
<button id="test-api" type="button">Test API</button>
<button
title="Check the status of the API"
id="test-api"
type="button">
Test API
</button>
</div>
<div class="row">
<div id="test-menu-response">Check now!</div>
<button id="test-menu" type="button">Test menu</button>
<button
title="Check the availability of the menu configuration"
id="test-menu"
type="button">
Test menu
</button>
</div>
</div>
</dialog>