Compare commits
36 Commits
fdf73563a3
...
v3.14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02a9472ac1 | ||
|
|
e0b5978406 | ||
|
|
c48d74138a | ||
|
|
3404c7f679 | ||
|
|
8c5bdbc1f8 | ||
|
|
a97440b997 | ||
|
|
17690d4349 | ||
|
|
b1fb5d9417 | ||
|
|
2fb59d2a56 | ||
|
|
3e3f60c47c | ||
|
|
7c4a23866c | ||
|
|
dbd2b4a568 | ||
|
|
ed89d1da74 | ||
|
|
799288f36f | ||
|
|
2a68fcc95d | ||
|
|
b781c054d2 | ||
|
|
cc32abab28 | ||
|
|
c8953021e3 | ||
|
|
5ddcc14c54 | ||
|
|
6d0f449904 | ||
|
|
07f3be0d26 | ||
|
|
2f76d68f1b | ||
|
|
53ed5083ce | ||
|
|
124d949faa | ||
|
|
ca854cab75 | ||
|
|
68e7761684 | ||
|
|
00649653bb | ||
|
|
8893e596ec | ||
|
|
e9bce71748 | ||
|
|
adc04ea9a2 | ||
|
|
7daa07f52f | ||
|
|
6721a13e99 | ||
|
|
b6cec5a6d4 | ||
|
|
75ccf7dc01 | ||
|
|
b94343de4f | ||
|
|
cc2fc27532 |
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -2,13 +2,19 @@
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
labels: question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
The majority of bug reports submitted are solved without changes to the Garmin application's code. This means that reporters are too often keen to attribute their issue to a problem with the application instead of:
|
||||
1. Their own lack of reading documentation
|
||||
2. Their own infrastructure
|
||||
|
||||
Therefore in the first place make your report as if it were a **question** in order to establish if the authors agree there is really a bug!
|
||||
|
||||
**Describe your issue**
|
||||
A clear and concise description of what the issue is.
|
||||
|
||||
**IMPORTANT!**
|
||||
I have:
|
||||
@@ -17,8 +23,9 @@ I have:
|
||||
- [ ] Checked that my JSON Syntax is correct ([options 2, 3, or 4](https://github.com/house-of-abbey/GarminHomeAssistant#editing-the-json-file) or otherwise)
|
||||
- [ ] Checked that my JSON follows the schema ([options 2, 3, or 4](https://github.com/house-of-abbey/GarminHomeAssistant#editing-the-json-file))
|
||||
- [ ] Used the online editor to check my JSON against HomeAssistant ([option 1](https://github.com/house-of-abbey/GarminHomeAssistant#editing-the-json-file))
|
||||
- [ ] Checked the [Docs](https://github.com/house-of-abbey/GarminHomeAssistant)
|
||||
- [ ] Checked the [Troubleshooting Guide](https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/TroubleShooting.md)
|
||||
- [ ] Read the [Documentation](https://github.com/house-of-abbey/GarminHomeAssistant)
|
||||
- [ ] Run through the [Troubleshooting Guide](https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/TroubleShooting.md)
|
||||
- [ ] Checked the [Known Issues](https://github.com/house-of-abbey/GarminHomeAssistant?tab=readme-ov-file#known-issues), i.e. the problems beyond the control of this Garmin application and must therefore be tolerated!
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1
.gitignore
vendored
@@ -7,3 +7,4 @@ Thumbs.db
|
||||
source/ClientId.mc
|
||||
# Gemini API key for automated translations
|
||||
gemini_api_key.txt
|
||||
developer_key
|
||||
@@ -33,11 +33,13 @@ From version 2.6 the application includes reporting your location. The location
|
||||
You get whatever your device provides at the moment, i.e. at the accuracy the device currently provides. If your watch is not calibrated you get poor data. It might mean that you get more accurate location data when you are in a location tracking activity (i.e. not swimming pool lengths). The device [indicates an accuracy](https://developer.garmin.com/connect-iq/api-docs/Toybox/Position.html#Quality-module) in units of:
|
||||
|
||||
- `Position.QUALITY_NOT_AVAILABLE` - No update provided
|
||||
- `Position.QUALITY_LAST_KNOWN` - No update provided
|
||||
- `Position.QUALITY_POOR` - We translate that to 500 m arbitrarily
|
||||
- `Position.QUALITY_USABLE` - We translate that to 100 m arbitrarily
|
||||
- `Position.QUALITY_LAST_KNOWN` - We translate that to 200 m arbitrarily
|
||||
- `Position.QUALITY_POOR` - We translate that to 100 m arbitrarily
|
||||
- `Position.QUALITY_USABLE` - We translate that to 50 m arbitrarily
|
||||
- `Position.QUALITY_GOOD` - We translate that to 10 m arbitrarily
|
||||
|
||||
NB. These values have been revised as of Ver 3.12.
|
||||
|
||||
**You cannot rely on the radius of the circle of accuracy in any resulting maps as any meaningful indication of error.**
|
||||
|
||||
## Activity Reporting
|
||||
|
||||
345
Devices.md
@@ -18,7 +18,7 @@ A user has reported a maximum of 26 items with Ver 3.5. This measurement has sho
|
||||
As a worked example, for Ver 3.6 working on an `instinct2x` device:
|
||||
|
||||
| Feature | Memory (bytes) | Cost (bytes) |
|
||||
|--------------------------------------|---------------:|-------------:|
|
||||
|:-------------------------------------|---------------:|-------------:|
|
||||
| Declared available to application | 98,304 | |
|
||||
| Measured available to application | 94,112 | (4,192 less) |
|
||||
| Application used | 65,696 | |
|
||||
@@ -28,167 +28,188 @@ As a worked example, for Ver 3.6 working on an `instinct2x` device:
|
||||
|
||||
Our test menu presently contains a mix of 28 items, consisting of nested group, toggle, tap, info and numeric items with templates. So each item requires (12,624 + 14,856) / 28 = 982 bytes.
|
||||
|
||||
## Glance Memory Usage
|
||||
|
||||
Using a `venu2` device the Glance view memory statistics are:
|
||||
|
||||
| Measure | Memory (bytes) |
|
||||
|:----------|----------------:|
|
||||
| Total | 61,344 |
|
||||
| Peak Used | 32,224 |
|
||||
| Free | 29,120 |
|
||||
|
||||
This means that for older devices listed below, with only 32 kB of Glance memory, the Glance view crashes with an "_Out Of Memory Error_". There is no opportunity in the code to intervene and no way to catch this fatal error. Nor is there any way to disable the Glance view on a device by device basis. Therefore, the only answer at present is to allow the Glance view to crash. It may display "HomeAssistant" as the Glance view text before crashing.
|
||||
|
||||
This problem has been explored via a [Github issue](https://github.com/house-of-abbey/GarminHomeAssistant/issues/347). This is now listed as a [known issue](./README.md#known-issues).
|
||||
|
||||
## Garmin Devices
|
||||
|
||||
The following table details all the devices as at 1 October 2025 and whether they are supported by Garmin HomeAssistant. The available application memory is also detailed so that it can be compared to an application version listed above. Of particular concern are the 'Instinct' range of devices, being the smallest we currently support. New feature requests are now being vetted against how they might affect our ability to support the 'Instinct' range of devices. At some point support may have to be withdrawn in order to allow the Garmin HomeAssistant application to grow further.
|
||||
The following table details all the devices as at March 2026 and whether they are supported by Garmin HomeAssistant. The available application and glance memory is also detailed so that it can be compared to an application version listed above. Of particular concern are the 'Instinct' range of devices, being the smallest we currently support. New feature requests are now being vetted against how they might affect our ability to support the 'Instinct' range of devices. At some point support may have to be withdrawn in order to allow the Garmin HomeAssistant application to grow further.
|
||||
|
||||
| Device | Supported | Application Memory |
|
||||
|----------------------------|:---------:|--------------------:|
|
||||
| d2bravo | N | 65,536 |
|
||||
| d2bravo_titanium | N | 65,536 |
|
||||
| fenix3 | N | 65,536 |
|
||||
| fenix3_hr | N | 65,536 |
|
||||
| fr230 | N | 65,536 |
|
||||
| fr235 | N | 65,536 |
|
||||
| fr630 | N | 65,536 |
|
||||
| fr920xt | N | 65,536 |
|
||||
| vivoactive | N | 65,536 |
|
||||
| descentg1 | Y | 98,304 |
|
||||
| instinct2 | Y | 98,304 |
|
||||
| instinct2s | Y | 98,304 |
|
||||
| instinct2x | Y | 98,304 |
|
||||
| instinctcrossover | Y | 98,304 |
|
||||
| approachs60 | N | 131,072 |
|
||||
| enduro | Y | 131,072 |
|
||||
| fenix5 | Y | 131,072 |
|
||||
| fenix5s | Y | 131,072 |
|
||||
| fenix6 | Y | 131,072 |
|
||||
| fenix6s | Y | 131,072 |
|
||||
| fenixchronos | Y | 131,072 |
|
||||
| fr245 | Y | 131,072 |
|
||||
| fr55 | Y | 131,072 |
|
||||
| fr645 | Y | 131,072 |
|
||||
| fr735xt | N | 131,072 |
|
||||
| fr935 | Y | 131,072 |
|
||||
| instinct3solar45mm | Y | 131,072 |
|
||||
| instincte40mm | Y | 131,072 |
|
||||
| instincte45mm | Y | 131,072 |
|
||||
| venusq | Y | 131,072 |
|
||||
| vivoactive3 | Y | 131,072 |
|
||||
| vivoactive3d | N | 131,072 |
|
||||
| vivoactive_hr | N | 131,072 |
|
||||
| edge_520 | N | 262,144 |
|
||||
| fr255 | Y | 524,288 |
|
||||
| fr255s | Y | 524,288 |
|
||||
| approachs50 | Y | 786,432 |
|
||||
| approachs7042mm | Y | 786,432 |
|
||||
| approachs7047mm | Y | 786,432 |
|
||||
| d2airx10 | Y | 786,432 |
|
||||
| d2mach1 | Y | 786,432 |
|
||||
| descentg2 | Y | 786,432 |
|
||||
| descentmk343mm | Y | 786,432 |
|
||||
| descentmk351mm | Y | 786,432 |
|
||||
| enduro3 | Y | 786,432 |
|
||||
| epix2 | Y | 786,432 |
|
||||
| epix2pro42mm | Y | 786,432 |
|
||||
| epix2pro47mm | Y | 786,432 |
|
||||
| epix2pro47mmsystem7preview | Y | 786,432 |
|
||||
| epix2pro51mm | Y | 786,432 |
|
||||
| fenix7 | Y | 786,432 |
|
||||
| fenix7pro | Y | 786,432 |
|
||||
| fenix7pronowifi | Y | 786,432 |
|
||||
| fenix7s | Y | 786,432 |
|
||||
| fenix7spro | Y | 786,432 |
|
||||
| fenix7x | Y | 786,432 |
|
||||
| fenix7xpro | Y | 786,432 |
|
||||
| fenix7xpronowifi | Y | 786,432 |
|
||||
| fenix843mm | Y | 786,432 |
|
||||
| fenix847mm | Y | 786,432 |
|
||||
| fenix8pro47mm | Y | 786,432 |
|
||||
| fenix8solar47mm | Y | 786,432 |
|
||||
| fenix8solar51mm | Y | 786,432 |
|
||||
| fenixe | Y | 786,432 |
|
||||
| fr165 | Y | 786,432 |
|
||||
| fr165m | Y | 786,432 |
|
||||
| fr255m | Y | 786,432 |
|
||||
| fr255sm | Y | 786,432 |
|
||||
| fr265 | Y | 786,432 |
|
||||
| fr265s | Y | 786,432 |
|
||||
| fr57042mm | Y | 786,432 |
|
||||
| fr57047mm | Y | 786,432 |
|
||||
| fr955 | Y | 786,432 |
|
||||
| fr965 | Y | 786,432 |
|
||||
| fr970 | Y | 786,432 |
|
||||
| instinct3amoled45mm | Y | 786,432 |
|
||||
| instinct3amoled50mm | Y | 786,432 |
|
||||
| instinctcrossoveramoled | Y | 786,432 |
|
||||
| marq2 | Y | 786,432 |
|
||||
| marq2aviator | Y | 786,432 |
|
||||
| system8preview | N | 786,432 |
|
||||
| venu2 | Y | 786,432 |
|
||||
| venu2plus | Y | 786,432 |
|
||||
| venu2s | Y | 786,432 |
|
||||
| venu3 | Y | 786,432 |
|
||||
| venu3s | Y | 786,432 |
|
||||
| venu441mm | Y | 786,432 |
|
||||
| venu445mm | Y | 786,432 |
|
||||
| venusq2 | Y | 786,432 |
|
||||
| venusq2m | Y | 786,432 |
|
||||
| venux1 | Y | 786,432 |
|
||||
| vivoactive5 | Y | 786,432 |
|
||||
| vivoactive6 | Y | 786,432 |
|
||||
| approachs62 | N | 1,048,576 |
|
||||
| d2air | Y | 1,048,576 |
|
||||
| edge1030 | Y | 1,048,576 |
|
||||
| edge1030bontrager | Y | 1,048,576 |
|
||||
| edge1030plus | Y | 1,048,576 |
|
||||
| edge1040 | Y | 1,048,576 |
|
||||
| edge1050 | Y | 1,048,576 |
|
||||
| edge520plus | Y | 1,048,576 |
|
||||
| edge530 | Y | 1,048,576 |
|
||||
| edge540 | Y | 1,048,576 |
|
||||
| edge550 | Y | 1,048,576 |
|
||||
| edge820 | Y | 1,048,576 |
|
||||
| edge830 | Y | 1,048,576 |
|
||||
| edge840 | Y | 1,048,576 |
|
||||
| edge850 | Y | 1,048,576 |
|
||||
| edgeexplore | Y | 1,048,576 |
|
||||
| edgeexplore2 | Y | 1,048,576 |
|
||||
| edgemtb | Y | 1,048,576 |
|
||||
| edge_1000 | N | 1,048,576 |
|
||||
| epix | N | 1,048,576 |
|
||||
| fr645m | Y | 1,048,576 |
|
||||
| legacyherocaptainmarvel | Y | 1,048,576 |
|
||||
| legacyherofirstavenger | Y | 1,048,576 |
|
||||
| legacysagadarthvader | Y | 1,048,576 |
|
||||
| legacysagarey | Y | 1,048,576 |
|
||||
| venu | Y | 1,048,576 |
|
||||
| venud | Y | 1,048,576 |
|
||||
| venusqm | Y | 1,048,576 |
|
||||
| vivoactive3m | Y | 1,048,576 |
|
||||
| vivoactive3mlte | Y | 1,048,576 |
|
||||
| vivoactive4 | Y | 1,048,576 |
|
||||
| vivoactive4s | Y | 1,048,576 |
|
||||
| d2charlie | N | 1,310,720 |
|
||||
| d2delta | Y | 1,310,720 |
|
||||
| d2deltapx | Y | 1,310,720 |
|
||||
| d2deltas | Y | 1,310,720 |
|
||||
| descentmk1 | N | 1,310,720 |
|
||||
| descentmk2 | Y | 1,310,720 |
|
||||
| descentmk2s | Y | 1,310,720 |
|
||||
| fenix5plus | Y | 1,310,720 |
|
||||
| fenix5splus | Y | 1,310,720 |
|
||||
| fenix5x | Y | 1,310,720 |
|
||||
| fenix5xplus | Y | 1,310,720 |
|
||||
| fenix6pro | Y | 1,310,720 |
|
||||
| fenix6spro | Y | 1,310,720 |
|
||||
| fenix6xpro | Y | 1,310,720 |
|
||||
| fr245m | Y | 1,310,720 |
|
||||
| fr745 | Y | 1,310,720 |
|
||||
| fr945 | Y | 1,310,720 |
|
||||
| fr945lte | Y | 1,310,720 |
|
||||
| marqadventurer | Y | 1,310,720 |
|
||||
| marqathlete | Y | 1,310,720 |
|
||||
| marqaviator | Y | 1,310,720 |
|
||||
| marqcaptain | Y | 1,310,720 |
|
||||
| marqcommander | Y | 1,310,720 |
|
||||
| marqdriver | Y | 1,310,720 |
|
||||
| marqexpedition | Y | 1,310,720 |
|
||||
| marqgolfer | Y | 1,310,720 |
|
||||
| gpsmap66 | Y | 2,359,296 |
|
||||
| gpsmap67 | Y | 2,359,296 |
|
||||
| gpsmap86 | N | 2,359,296 |
|
||||
| gpsmaph1 | Y | 2,359,296 |
|
||||
| montana7xx | Y | 2,359,296 |
|
||||
| oregon7xx | N | 2,359,296 |
|
||||
| rino7xx | N | 2,359,296 |
|
||||
| Device | Supported | Application Memory | Glance Memory |
|
||||
|:---------------------------|:---------:|-------------------:|---------------:|
|
||||
| approachs50 | Y | 786,432 | 65,536 |
|
||||
| approachs60 | N | 131,072 | |
|
||||
| approachs62 | N | 1,048,576 | |
|
||||
| approachs7042mm | Y | 786,432 | 65,536 |
|
||||
| approachs7047mm | Y | 786,432 | 65,536 |
|
||||
| d2air | Y | 1,048,576 | |
|
||||
| d2airx10 | Y | 786,432 | 65,536 |
|
||||
| d2bravo | N | 65,536 | |
|
||||
| d2bravo_titanium | N | 65,536 | |
|
||||
| d2charlie | N | 1,310,720 | |
|
||||
| d2delta | Y | 1,310,720 | |
|
||||
| d2deltapx | Y | 1,310,720 | |
|
||||
| d2deltas | Y | 1,310,720 | |
|
||||
| d2mach1 | Y | 786,432 | 65,536 |
|
||||
| d2mach2 | Y | 786,432 | 65,536 |
|
||||
| descentg1 | Y | 98,304 | 32,768 |
|
||||
| descentg2 | Y | 786,432 | 65,536 |
|
||||
| descentmk1 | N | 1,310,720 | |
|
||||
| descentmk2 | Y | 1,310,720 | 32,768 |
|
||||
| descentmk2s | Y | 1,310,720 | 32,768 |
|
||||
| descentmk343mm | Y | 786,432 | 65,536 |
|
||||
| descentmk351mm | Y | 786,432 | 65,536 |
|
||||
| edge1030 | Y | 1,048,576 | |
|
||||
| edge1030bontrager | Y | 1,048,576 | |
|
||||
| edge1030plus | Y | 1,048,576 | |
|
||||
| edge1040 | Y | 1,048,576 | 65,536 |
|
||||
| edge1050 | Y | 1,048,576 | 65,536 |
|
||||
| edge130 | N | | |
|
||||
| edge130plus | N | | |
|
||||
| edge520plus | Y | 1,048,576 | |
|
||||
| edge530 | Y | 1,048,576 | |
|
||||
| edge540 | Y | 1,048,576 | 65,536 |
|
||||
| edge550 | Y | 1,048,576 | 65,536 |
|
||||
| edge820 | Y | 1,048,576 | |
|
||||
| edge830 | Y | 1,048,576 | |
|
||||
| edge840 | Y | 1,048,576 | 65,536 |
|
||||
| edge850 | Y | 1,048,576 | 65,536 |
|
||||
| edgeexplore | Y | 1,048,576 | |
|
||||
| edgeexplore2 | Y | 1,048,576 | 65,536 |
|
||||
| edgemtb | Y | 1,048,576 | 65,536 |
|
||||
| edge_1000 | N | 1,048,576 | |
|
||||
| edge_520 | N | 262,144 | |
|
||||
| enduro | Y | 131,072 | 32,768 |
|
||||
| enduro3 | Y | 786,432 | 65,536 |
|
||||
| epix | N | 1,048,576 | |
|
||||
| epix2 | Y | 786,432 | 65,536 |
|
||||
| epix2pro42mm | Y | 786,432 | 65,536 |
|
||||
| epix2pro47mm | Y | 786,432 | 65,536 |
|
||||
| epix2pro47mmsystem7preview | Y | 786,432 | 65,536 |
|
||||
| epix2pro51mm | Y | 786,432 | 65,536 |
|
||||
| etrextouch | Y | 2,359,296 | |
|
||||
| fenix3 | N | 65,536 | |
|
||||
| fenix3_hr | N | 65,536 | |
|
||||
| fenix5 | Y | 131,072 | |
|
||||
| fenix5plus | Y | 1,310,720 | |
|
||||
| fenix5s | Y | 131,072 | |
|
||||
| fenix5splus | Y | 1,310,720 | |
|
||||
| fenix5x | Y | 1,310,720 | |
|
||||
| fenix5xplus | Y | 1,310,720 | |
|
||||
| fenix6 | Y | 131,072 | 32,768 |
|
||||
| fenix6pro | Y | 1,310,720 | 32,768 |
|
||||
| fenix6s | Y | 131,072 | 32,768 |
|
||||
| fenix6spro | Y | 1,310,720 | 32,768 |
|
||||
| fenix6xpro | Y | 1,310,720 | 32,768 |
|
||||
| fenix7 | Y | 786,432 | 65,536 |
|
||||
| fenix7pro | Y | 786,432 | 65,536 |
|
||||
| fenix7pronowifi | Y | 786,432 | 65,536 |
|
||||
| fenix7s | Y | 786,432 | 65,536 |
|
||||
| fenix7spro | Y | 786,432 | 65,536 |
|
||||
| fenix7x | Y | 786,432 | 65,536 |
|
||||
| fenix7xpro | Y | 786,432 | 65,536 |
|
||||
| fenix7xpronowifi | Y | 786,432 | 65,536 |
|
||||
| fenix843mm | Y | 786,432 | 65,536 |
|
||||
| fenix847mm | Y | 786,432 | 65,536 |
|
||||
| fenix8pro47mm | Y | 786,432 | 65,536 |
|
||||
| fenix8solar47mm | Y | 786,432 | 65,536 |
|
||||
| fenix8solar51mm | Y | 786,432 | 65,536 |
|
||||
| fenixchronos | Y | 131,072 | |
|
||||
| fenixe | Y | 786,432 | 65,536 |
|
||||
| fr165 | Y | 786,432 | 65,536 |
|
||||
| fr165m | Y | 786,432 | 65,536 |
|
||||
| fr230 | N | 65,536 | |
|
||||
| fr235 | N | 65,536 | |
|
||||
| fr245 | Y | 131,072 | 32,768 |
|
||||
| fr245m | Y | 1,310,720 | 32,768 |
|
||||
| fr255 | Y | 524,288 | 65,536 |
|
||||
| fr255m | Y | 786,432 | 65,536 |
|
||||
| fr255s | Y | 524,288 | 65,536 |
|
||||
| fr255sm | Y | 786,432 | 65,536 |
|
||||
| fr265 | Y | 786,432 | 65,536 |
|
||||
| fr265s | Y | 786,432 | 65,536 |
|
||||
| fr45 | N | | |
|
||||
| fr55 | Y | 131,072 | 32,768 |
|
||||
| fr57042mm | Y | 786,432 | 65,536 |
|
||||
| fr57047mm | Y | 786,432 | 65,536 |
|
||||
| fr630 | N | 65,536 | |
|
||||
| fr645 | Y | 131,072 | |
|
||||
| fr645m | Y | 1,048,576 | |
|
||||
| fr735xt | N | 131,072 | |
|
||||
| fr745 | Y | 1,310,720 | 32,768 |
|
||||
| fr920xt | N | 65,536 | |
|
||||
| fr935 | Y | 131,072 | |
|
||||
| fr945 | Y | 1,310,720 | 32,768 |
|
||||
| fr945lte | Y | 1,310,720 | 32,768 |
|
||||
| fr955 | Y | 786,432 | 65,536 |
|
||||
| fr965 | Y | 786,432 | 65,536 |
|
||||
| fr970 | Y | 786,432 | 65,536 |
|
||||
| garminswim2 | N | | |
|
||||
| gpsmap66 | Y | 2,359,296 | |
|
||||
| gpsmap67 | Y | 2,359,296 | |
|
||||
| gpsmap86 | N | 2,359,296 | |
|
||||
| gpsmaph1 | Y | 2,359,296 | |
|
||||
| instinct2 | Y | 98,304 | 32,768 |
|
||||
| instinct2s | Y | 98,304 | 32,768 |
|
||||
| instinct2x | Y | 98,304 | 32,768 |
|
||||
| instinct3amoled45mm | Y | 786,432 | 65,536 |
|
||||
| instinct3amoled50mm | Y | 786,432 | 65,536 |
|
||||
| instinct3solar45mm | Y | 131,072 | 32,768 |
|
||||
| instinctcrossover | Y | 98,304 | 32,768 |
|
||||
| instinctcrossoveramoled | Y | 786,432 | 65,536 |
|
||||
| instincte40mm | Y | 131,072 | 32,768 |
|
||||
| instincte45mm | Y | 131,072 | 32,768 |
|
||||
| legacyherocaptainmarvel | Y | 1,048,576 | |
|
||||
| legacyherofirstavenger | Y | 1,048,576 | |
|
||||
| legacysagadarthvader | Y | 1,048,576 | |
|
||||
| legacysagarey | Y | 1,048,576 | |
|
||||
| marq2 | Y | 786,432 | 65,536 |
|
||||
| marq2aviator | Y | 786,432 | 65,536 |
|
||||
| marqadventurer | Y | 1,310,720 | 32,768 |
|
||||
| marqathlete | Y | 1,310,720 | 32,768 |
|
||||
| marqaviator | Y | 1,310,720 | 32,768 |
|
||||
| marqcaptain | Y | 1,310,720 | 32,768 |
|
||||
| marqcommander | Y | 1,310,720 | 32,768 |
|
||||
| marqdriver | Y | 1,310,720 | 32,768 |
|
||||
| marqexpedition | Y | 1,310,720 | 32,768 |
|
||||
| marqgolfer | Y | 1,310,720 | 32,768 |
|
||||
| montana7xx | Y | 2,359,296 | |
|
||||
| oregon7xx | N | 2,359,296 | |
|
||||
| rino7xx | N | 2,359,296 | |
|
||||
| system8preview | N | 786,432 | 65,536 |
|
||||
| venu | Y | 1,048,576 | |
|
||||
| venu2 | Y | 786,432 | 65,536 |
|
||||
| venu2plus | Y | 786,432 | 65,536 |
|
||||
| venu2s | Y | 786,432 | 65,536 |
|
||||
| venu3 | Y | 786,432 | 65,536 |
|
||||
| venu3s | Y | 786,432 | 65,536 |
|
||||
| venu441mm | Y | 786,432 | 65,536 |
|
||||
| venu445mm | Y | 786,432 | 65,536 |
|
||||
| venud | Y | 1,048,576 | |
|
||||
| venusq | Y | 131,072 | |
|
||||
| venusq2 | Y | 786,432 | 65,536 |
|
||||
| venusq2m | Y | 786,432 | 65,536 |
|
||||
| venusqm | Y | 1,048,576 | |
|
||||
| venux1 | Y | 786,432 | 65,536 |
|
||||
| vivoactive | N | 65,536 | |
|
||||
| vivoactive3 | Y | 131,072 | |
|
||||
| vivoactive3d | N | 131,072 | |
|
||||
| vivoactive3m | Y | 1,048,576 | |
|
||||
| vivoactive3mlte | Y | 1,048,576 | |
|
||||
| vivoactive4 | Y | 1,048,576 | |
|
||||
| vivoactive4s | Y | 1,048,576 | |
|
||||
| vivoactive5 | Y | 786,432 | 65,536 |
|
||||
| vivoactive6 | Y | 786,432 | 65,536 |
|
||||
| vivoactive_hr | N | 131,072 | |
|
||||
| vivolife | N | | |
|
||||
@@ -142,6 +142,8 @@ On Mon, 6 May 2024 at 09:01, <noreply@garmin.com> wrote:
|
||||
|
||||

|
||||
|
||||
<img src="images/etiquette/2026-04-02_GHA_Bad_Review.png" width="70%" title="Not working">
|
||||
|
||||
Answer: Make sure you meet the [minimum requirements for being a user](#etiquette) please! Provide more information on what the problem actually is. Clearly the support reply pointing to the documentation was either not read or not considered helpful, hence the point about meeting the minimum user requirements.
|
||||
|
||||
On Fri, 31 Oct 2025 at 11:45, <noreply@garmin.com> wrote:
|
||||
@@ -390,10 +392,10 @@ Don't get an embarrassing retort, I call out those who "click & complain" withou
|
||||
|
||||
Two words. Perhaps too lazy to consider why others gave rave reviews and ponder what he was doing wrong? Perhaps did not consider there might be some documentation to assist or support routes (subject to [getting a polite reply](getting-help-with-a-polite-reply))?
|
||||
|
||||
<img src="images/etiquette/2026-01-21 GHA_Bad_Review.png" width="70%" title="Two words">
|
||||
<img src="images/etiquette/2026-01-21_GHA_Bad_Review.png" width="70%" title="Two words">
|
||||
|
||||
---
|
||||
|
||||
Blaming the wrong application. Just rude. Please get your facts straight. And it was such a simple workaround for the Garmin Connect IQ app bug.
|
||||
|
||||
<img src="images/etiquette/2025-11-19%20GHA_Bad_Review.png" width="85%" title="Misplaced blame">
|
||||
<img src="images/etiquette/2025-11-19_GHA_Bad_Review.png" width="85%" title="Misplaced blame">
|
||||
|
||||
13
HISTORY.md
@@ -36,13 +36,13 @@
|
||||
| 2.21 | Added 7 new devices (`edge1050`, `enduro3`, `fenix843mm`, `fenix847mm`, `fenix8solar47mm`, `fenix8solar51mm`, `fenixe`) and upgraded the SDK to 7.3.0. Fix for a bug on Edge devices introduced by v2.16 activity reporting improvements. |
|
||||
| 2.22 | <img src="images/pin_view.png" width="200" title="PIN Entry View"/><br/>Major feature release adding an optional PIN to menu items. This significant new feature has been provided by [@moesterheld](https://github.com/moesterheld). Please do not rely on this application for security. Use at your own risk! |
|
||||
| 2.23 | Added "info" menu item for displaying information via a template without a tap or toggle. Essentially like the old 'template' type that was deprecated when all items were amended to display evaluated templates. That action removed the display only items too hastily. Added 5 new devices in the model range Instinct 3 and Instinct E. |
|
||||
| 2.24 | Experiment to prevent new Webhook IDs being created unnecessarily. Reduced the latency for the first menu update. Added 4 new devices: approachs50, descentg2, descentmk1, and gpsmap66. |
|
||||
| 2.24 | Experiment to prevent new Webhook IDs being created unnecessarily. Reduced the latency for the first menu update. Added 4 new devices: `approachs50`, `descentg2`, ~~`descentmk1`~~ (subsequently withdrawn in v3.6), and `gpsmap66`. |
|
||||
| 2.25 | 2 Bug fixes. First time startup issues caused by v2.24 change and a fix for pure numbers in returned templates. |
|
||||
| 2.26 | Retry responsive menu fix failed in v2.24. Cosmetic internal class changes. |
|
||||
| 2.27 | Trivial bug fix for the glance view to prevent the "Unconfigured" result being erroneously displayed because the settings were not yet pulled from persistent storage. |
|
||||
| 2.28 | Added support for Vivoactive 6 device which also required an SDK update to 8.1.0. |
|
||||
| 2.29 | Added support for three new devices, Forerunners 570 42mm & 47mm and 970. |
|
||||
| 2.30 | <img src="images/Venu2_glance_default.png" width="200" title="Default Glance"/><br/>Extensive re-work of the [Glance](examples/Glance.md) view, including the ability to customise it with a user supplied template. |
|
||||
| 2.30 | <img src="images/Venu2_glance2.png" width="200" title="Default Glance"/><br/>Extensive re-work of the [Glance](examples/Glance.md) view, including the ability to customise it with a user supplied template. |
|
||||
| 2.31 | Adding [two new options](./examples/Actions.md#exit-on-tap) to the menu items: 1) The ability to disable a menu item, e.g. temporarily for seasonal changes, 2) The option to exit after a menu item has been select. |
|
||||
| 2.32 | Bug fix for a breaking change extracting options caused by the need to rearrange function parameters for an [annoying compiler error](https://github.com/house-of-abbey/GarminHomeAssistant/issues/253). |
|
||||
| 3.0 | First version with the ability to use [Wi-Fi or LTE](Wi-Fi.md) instead of Bluetooth but with limited functionality, thanks to [@vincentezw](https://github.com/vincentezw). |
|
||||
@@ -51,9 +51,12 @@
|
||||
| 3.3 | Providing automatic detection for menu definition updates, but still requires an application restart. |
|
||||
| 3.4 | Fixed a bug where templates failed to display in toggle menu items (at least on some devices). Fixed a bug where a menu item requesting to exit on completion appeared to indicate failure when using Wi-Fi or LTE. The fix uses a delay in exiting the application modelled as sufficient for a Venu 2 device, so this might need tweaking for other devices. Attempt to fixed an "Out of Memory" bug caused by v3.3 by making automatic checking for menu updates both optional and automatically turned off when insufficient memory is available. This last bug is device dependent and may require another attempt. Internationalisation improvements with thanks to [@krzys_h](https://github.com/krzys-h) for a new automated translations script. |
|
||||
| 3.5 | Added support for Edge 550, 850 & MTB, Fenix 8 Pro 47mm, GPSMAP H1, Instinct Crossover AMOLED, Venu 4 41mm & 45mm, & Venu X1 devices which also required an SDK update to 8.3.0. The simulation of the Edge 850 device was off, as it failed to update the display and text was the wrong colour, but the buttons menu items operated HA correctly. The assumption is the simulation model is buggy until someone [reports](https://github.com/house-of-abbey/GarminHomeAssistant/issues) otherwise. |
|
||||
| 3.6 | Added `numeric` menu item type thanks to [@thmichel](https://github.com/thmichel). This allows you to select a numeric value to set for an entity. Confirmations can now display a user supplied message. [Schema update](README.md#old-deprecated-formats) to keep pace with HomeAssistant and correct a previous decision. Schema changes for consistency. |
|
||||
| 3.6 | Added `numeric` menu item type thanks to [@thmichel](https://github.com/thmichel). This allows you to select a numeric value to set for an entity. Confirmations can now display a user supplied message. [Schema update](README.md#old-deprecated-formats) to keep pace with HomeAssistant and correct a previous decision. Schema changes for consistency. `descentmk1` device removed as the simulation does not read the settings and therefore we cannot support the device. |
|
||||
| 3.7 | Bug fix for `numeric` menu items not working over Wi-Fi & LTE. |
|
||||
| 3.8 | Added icon for `numeric` menu items and revised icons in general. |
|
||||
| 3.9 | Small update to warn users with empty menu definitions to read the instructions! |
|
||||
| 3.10 | Bug avoidance for a fatal "out of memory error" in the glance when caching the menu to [`Storage`](https://developer.garmin.com/connect-iq/api-docs/Toybox/Application/Storage.html). This is now delayed until the main application is opened. Added support for two new devices, D2 Mach 2 and eTrex Touch. |
|
||||
| 3.11 | <img src="images/Venu2_glance_default.png" width="200" title="Default Glance"/><br/>Better handling of menus larger than the Glance view can handle. As a result the Glance can no longer verify the availability of the JSON menu as fetching it might cause an _"Error: Out Of Memory Error"_. Added a second optional user supplied HTTP Header name & value for those who need to supply a username and a secret for their setup. |
|
||||
| 3.10 | Bug avoidance for a fatal _"Error: Out Of Memory Error"_ in the glance when caching the menu to [`Storage`](https://developer.garmin.com/connect-iq/api-docs/Toybox/Application/Storage.html). This is now delayed until the main application is opened. Added support for two new devices, D2 Mach 2 and eTrex Touch. |
|
||||
| 3.11 | <img src="images/Venu2_glance_default.png" width="200" title="Default Glance"/><br/>Better handling of menus larger than the Glance view can handle. As a result the Glance can no longer verify the availability of the JSON menu as fetching it might cause an _"Error: Out Of Memory Error"_. Provide two user supplied HTTP Headers. Bug fix for the numeric picker. |
|
||||
| 3.12 | Amended GPS accuracy values used by the background service. |
|
||||
| 3.13 | Added support for new devices: `d2mach2pro`, `fr170`, `fr170m`, `fr70`. |
|
||||
| 3.14 | Numeric items now support `tap_action.data` fields so service call options like `transition` are catered for. See the documentation on [Additional Action Data](https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/examples/Numeric.md#additional-action-data). This features has been provided by [@StumblingGamer](https://github.com/StumblingGamer). Now prevent PIN enabled menu items from being enabled on non-touch screen devices. The PIN must be removed for the menu item to function. Fix for initialising numeric pickers with no user defined template. |
|
||||
@@ -34,7 +34,7 @@ As of version 2.0, there are now two installable versions. For older devices bef
|
||||
|
||||
| Version | Explanation |
|
||||
|------------------------|-------------|
|
||||
| Application (original) | For newer devices that allow glance views in their applications, e.g. Venu 2, the GarminHomeAssistant application can be started either from a glance or from the list of applications and activities. Head over to the [GarminHomeAssistant](https://apps.garmin.com/en-US/apps/61c91d28-ec5e-438d-9f83-39e9f45b199d) application page on the [Connect IQ application store](https://apps.garmin.com/en-US/) to download the application. The application can be started two different ways, either from the glance in the carousel, or as an application from the list of applications & activities. With the latter, it is worth marking the application as a favourite.<br/><img src="images/Venu2_app_start.png" width="200" title="Venu 2" style="margin:5px"/><img src="images/Vivoactive3_app_start.jpg" width="200" title="Vivoactive 3" style="margin:5px"/><br/>If you place the application on your list of favourites, and rearrange it to appear near the top, then the item is just one button press away from the watch face. This second picture here shows the application menu on a Vivoactive 3 watch.<br/><img src="images/Venu2_glance_default.png" width="200" title="Venu 2" style="margin:5px"/><br/>On newer watches, you can also start the application from the glance carousel. The glance view here typically displays some trackable status, so ours provides some early indication of availability. Older watches will still allow you to start this application from the list of applications and activities. |
|
||||
| Application (original) | For newer devices that allow glance views in their applications, e.g. Venu 2, the GarminHomeAssistant application can be started either from a glance or from the list of applications and activities. Head over to the [GarminHomeAssistant](https://apps.garmin.com/en-US/apps/61c91d28-ec5e-438d-9f83-39e9f45b199d) application page on the [Connect IQ application store](https://apps.garmin.com/en-US/) to download the application. The application can be started two different ways, either from the glance in the carousel, or as an application from the list of applications & activities. With the latter, it is worth marking the application as a favourite.<br/><img src="images/Venu2_app_start.png" width="200" title="Venu 2" style="margin:5px"/><img src="images/Vivoactive3_app_start.jpg" width="200" title="Vivoactive 3" style="margin:5px"/><br/>If you place the application on your list of favourites, and rearrange it to appear near the top, then the item is just one button press away from the watch face. This second picture here shows the application menu on a Vivoactive 3 watch.<br/><img src="./images/Venu2_glance_custom.png" width="200" title="Venu 2" style="margin:5px"/><br/>On newer watches, you can also start the application from the glance carousel. The glance view here typically displays some trackable status, so ours provides some early indication of API availability or something custom as shown here. Older watches will still allow you to start this application from the list of applications and activities. |
|
||||
| Widget | **"Maintenance only mode"** so no new features will be added to this version.<br>For older devices that use widgets, e.g. Venu (1) as opposed to applications with "glances", the GarminHomeAssistant application can instead be started from the widget carousel. This is a separate item in the Connect IQ AppStore and with this installation, the application will no longer appear in the list of applications and activities. Head over to the [GarminHomeAssistant](https://apps.garmin.com/en-US/apps/) widget page on the [Connect IQ application store](https://apps.garmin.com/en-US/) to download the widget.<br/><img src="images/Venu_Widget_sim.png" width="200" title="Venu 2" style="margin:5px"/><br/>Typically the widget view implements something similar to the glance view, e.g. status, and exists in a widget carousel to allow you to select an application to launch.<br>**Please note that memory in widgets is more limited than applications. This means a large menu definition can crash the widget without the code catching the error.**<br> This version was born out of the application version and from Ver 2.0 shared the same source code repository until Ver 2.8 when they were [separated](https://github.com/house-of-abbey/GarminHomeAssistantWidget) to allow the application version to take advantage of its increase memory availability. |
|
||||
|
||||
### Features
|
||||
@@ -467,7 +467,7 @@ Check the latest unresolved [issues](https://github.com/house-of-abbey/GarminHom
|
||||
|
||||
10. There are memory limits, particularly for older devices. Please see the [explanation of the memory limits](Devices.md) and device support.
|
||||
|
||||
11. There is a 32 kB memory limit for any Glance views that mean too large a menu definition can cause the Glance view to crash. The symptom is that a previously 'Available' menu (green text) now appears as 'Unavailable' (red text) because the HTTP request to both retrieve the menu and convert it to `Dictionary` objects (from which the glance view can extract the `glance` field) ran out of memory. Out of memory exceptions are fatal and cannot be caught in order to code around them. Therefore the Glance status does not get updated to 'Available' as you would expect. The solution is to limit the size of your JSON menu definition by experimentation. For the same reason, the Glance view can no longer cache the menu to `Storage` ahead of the main application as the keys in that storage are limited to 8 kB. Remember its an app running on your watch not a desktop!
|
||||
11. There is a 32 kB memory limit for Glance views on some devices that mean the code and data for the Glance view is too large to load and the Glance view crashes before any code can intervene to prevent the fatal "Out Of Memory Error". Nor is it possible to disable the Glance view in the [application manifest](./manifest.xml) on a per device basis. The simple advice is, for older watches affected, do not try to use the Glance view. To see if your device is affected, please trawl the [devices page](./Devices.md).
|
||||
|
||||
# Authors & Contributors
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ pause
|
||||
#### API: On-line
|
||||
|
||||
There's an online way of testing the API URL too, thanks to [REQBIN](https://reqbin.com/post-online). This has less setup and it can be saved if you log into the web site. Please note two things:
|
||||
|
||||
1. The URL entere below must include a trailing '/' unlike the URL entered into the watch settings.
|
||||
2. The service imposes a limit on the number requests you can submit without a subscription, so click with purpose! NB. Changing browser buys you a few more clicks.
|
||||
|
||||
@@ -125,13 +126,10 @@ There's an online way of testing the API URL too, thanks to [REQBIN](https://req
|
||||
#### SSL Certificate Chain
|
||||
|
||||
With thanks to [@ziceva](https://github.com/ziceva) for solving this problem. The symptoms are:
|
||||
|
||||
1. Using an API URL with SSL (HTTPS), the [web-based editor](https://house-of-abbey.github.io/GarminHomeAssistant/web/) running in a browser on the same phone running Garmin Connect works well.
|
||||
2. The exact same configuration is set in the Garmin HomeAssistant application.
|
||||
3. The Garmin HomeAssistant application reports:
|
||||
```
|
||||
API: not available
|
||||
Menu: not available
|
||||
```
|
||||
3. The Garmin HomeAssistant application reports neither the API nor the menu are available.
|
||||
|
||||
**Solution: Make sure you use a _full chain_ certificate in your HTTPS proxy as some watches might be unable to validate the site certificate alone.**
|
||||
|
||||
@@ -147,7 +145,6 @@ To verify if you have this issue you can use a tool like [SSL Shoppers's SSL Che
|
||||
|
||||

|
||||
|
||||
|
||||
### Top Problems
|
||||
|
||||
1. Failure to copy & paste keys and URLs leading to minor and hard to see errors in strings, even with protestations they are the same! (No they weren't...)
|
||||
@@ -158,17 +155,11 @@ To verify if you have this issue you can use a tool like [SSL Shoppers's SSL Che
|
||||
The [editor](https://house-of-abbey.github.io/GarminHomeAssistant/web/) provides the following functions:
|
||||
|
||||
1. Syntax highlighting
|
||||
|
||||
2. Schema checking
|
||||
|
||||

|
||||
|
||||
3. Entity name completion
|
||||
|
||||

|
||||
|
||||
4. Rendering previews, to aid HA 'template' creation
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -185,6 +176,7 @@ On (1) you will initially be presented with the following message. Please do not
|
||||
<div style="margin:30px;padding:20px;background-color:lightgrey;border:1px solid black;border-radius: 10px;">
|
||||
|
||||
# GarminHomeAssistant Web Editor
|
||||
|
||||
This is the web editor for the GarminHomeAssistant watch app, it offers enhanced schema checking and validation over the original JSON schema by using the HomeAssistant API to create a schema based on your HomeAssistant configuration.
|
||||
|
||||
This editor makes use of the same credentials as the watch app (these can be pasted in the top bar of this page). However in order for this editor to work, you will need to amend the CORS settings of your HomeAssistant instance. Add this to your configuration.yaml file:
|
||||
@@ -215,7 +207,7 @@ For directions on how to write your menu.json file, please see the README in the
|
||||
|
||||
For this you will need to have already got the main application or widget working with a menu in order to prove that the API calls are successful. We have proven this works with both our home brew infrastructure as well as Nabu Casa. Now with a script similar to one of the following two, you should be able to fake the watch API call and verify receipt by HomeAssistant.
|
||||
|
||||
#### Battery: Linux, MacOS, UNIX, Cygwin etc
|
||||
### Battery: Linux, MacOS, UNIX, Cygwin etc
|
||||
|
||||
Assume a file called: `send_battery.bash`
|
||||
|
||||
@@ -248,13 +240,13 @@ curl -s -X POST \
|
||||
|
||||
Execute:
|
||||
|
||||
```
|
||||
```text
|
||||
$ ./send_battery.bash 45 1
|
||||
```
|
||||
|
||||
The output looks like this:
|
||||
|
||||
```
|
||||
```text
|
||||
Battery Level = 45
|
||||
Battery Charging? = true
|
||||
|
||||
@@ -315,13 +307,13 @@ pause
|
||||
|
||||
Execute:
|
||||
|
||||
```
|
||||
```text
|
||||
> home_assistant_battery_level.cmd 41 1
|
||||
```
|
||||
|
||||
The output looks like this:
|
||||
|
||||
```
|
||||
```text
|
||||
"Battery Level = 41"
|
||||
"Battery Charging? = true"
|
||||
|
||||
@@ -345,7 +337,7 @@ There's an online way of testing the API URL too, thanks to [REQBIN](https://req
|
||||
|
||||
URL for copy & paste:
|
||||
|
||||
```
|
||||
```text
|
||||
https://<Your Domain>/api/webhook/<Your Webhook ID>
|
||||
```
|
||||
|
||||
@@ -399,7 +391,7 @@ As a desperate measure to assist with debugging the HomeAssistant Application, y
|
||||
|
||||
The figure above shows how to find the file on Windows by attaching your watch by USB cable. Inside the `CIQ_LOG.YML` file there are often multiple entries, each looking like this:
|
||||
|
||||
```
|
||||
```text
|
||||
Error: Unexpected Type Error
|
||||
Details: 'Failed invoking <symbol>'
|
||||
Time: 2024-08-30T12:00:25Z
|
||||
|
||||
@@ -251,11 +251,17 @@
|
||||
"$ref": "#/$defs/content"
|
||||
},
|
||||
"tap_action": {
|
||||
"$ref": "#/$defs/tap_action",
|
||||
"title": "Tap Action",
|
||||
"description": "Numeric tap action definition to include the number picker. 'confirm', 'pin' and 'exit' fields are optional.",
|
||||
"properties": {
|
||||
"action": {
|
||||
"$ref": "#/$defs/action"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"title": "Additional action data",
|
||||
"description": "Fixed Home Assistant service data. The selected picker value is merged into this object using the picker's data_attribute. Service-specific fields cannot be schema checked here."
|
||||
},
|
||||
"picker": {
|
||||
"type": "object",
|
||||
"title": "Number picker configuration",
|
||||
@@ -289,13 +295,24 @@
|
||||
"max",
|
||||
"step",
|
||||
"data_attribute"
|
||||
]
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"confirm": {
|
||||
"$ref": "#/$defs/confirm"
|
||||
},
|
||||
"pin": {
|
||||
"$ref": "#/$defs/pin"
|
||||
},
|
||||
"exit": {
|
||||
"$ref": "#/$defs/exit"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"action",
|
||||
"picker"
|
||||
]
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"enabled": {
|
||||
"$ref": "#/$defs/enabled"
|
||||
@@ -982,7 +999,7 @@
|
||||
"tap_action": {
|
||||
"type": "object",
|
||||
"title": "Tap Action",
|
||||
"description": "'confirm' and 'pin' fields are optional.",
|
||||
"description": "'confirm', 'pin' and 'exit' fields are optional.",
|
||||
"properties": {
|
||||
"confirm": {
|
||||
"$ref": "#/$defs/confirm"
|
||||
|
||||
@@ -25,6 +25,8 @@ Two simple examples using a scene and a cover as `tap` menu items.
|
||||
}
|
||||
```
|
||||
|
||||
## Confirmation
|
||||
|
||||
Any menu item with an action (`tap`, `template`, or `toggle`), may have a confirmation view added. For consistency this is always done via the `tap_action` JSON object, even though for a `toggle` menu item there will only ever be a single field inside. For the `toggle` menu item, the confirmation is presented on both `on` and `off` directions. There is no option for asymmetry, i.e. only in one direction.
|
||||
|
||||
```json
|
||||
@@ -56,6 +58,8 @@ The `confirm` field may contain a string instead of a Boolean in order to provid
|
||||
}
|
||||
```
|
||||
|
||||
## Personal Identification Number (PIN)
|
||||
|
||||
**The authors do not advise the use of this application for security sensitive devices. But we suspect users are taking that risk anyway, hence a PIN confirmation is provided that can be used for additional menu item security.**
|
||||
|
||||
This can be enabled by setting the `pin` field in the `tap_action`. The `pin` field overrides `confirm`. Explicitly setting `confirm` is not necessary.
|
||||
@@ -70,9 +74,14 @@ The 4-digit PIN is set globally for all actions in the app settings in Connect I
|
||||
|
||||
When entering an invalid PIN for the fifth time within 2 minutes, the PIN dialog will be locked for all actions for the next 10 minutes. Entering a valid PIN will always reset the failure counter.
|
||||
|
||||
<img src="../images/pin_view.png" width="200" title="Confirmation View"/>
|
||||
<img src="../images/pin_view.png" width="200" title="PIN View"/>
|
||||
|
||||
Note that for notify events, you _must_ not supply an `entity_id` or the API call will fail. There are other examples too.
|
||||
> [!IMPORTANT]
|
||||
> The PIN entry requires a touch screen enabled device.
|
||||
|
||||
## Additional Data
|
||||
|
||||
Some `action`s require additional `data` to be supplied. The following example shows how we handle this scenario. Note that for notify events, you _must not_ supply an `entity_id` or the API call will fail. There are other examples too.
|
||||
|
||||
```json
|
||||
{
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
# Glance
|
||||
|
||||
Since [version 2.30](../History.md), it is possible to ovverride the text displayed on the Glance view. This page explains how to customise the text.
|
||||
Since [version 2.30](../History.md), it is possible to override the text displayed on the Glance view. This page explains how to customise the text.
|
||||
|
||||
## Default View
|
||||
## Status View
|
||||
|
||||
The default view has always been to display the status of the menu and API availability to indicate if there's a problem. This view has now been updated to be more colourful.
|
||||
The status view displays the accessibility of HomeAssistant API to indicate if there's a problem.
|
||||
|
||||
<img src="../images/Venu2_glance_default.png" width="200" title="Venu 2 Default Glance"/>
|
||||
|
||||
When either the API or the menu file is inaccessible, the fields will turn red.
|
||||
When API is inaccessible the field will turn red.
|
||||
|
||||
## Customised View
|
||||
|
||||
@@ -43,12 +43,12 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
You may make this as complicated as you like! But you have limited space and only ASCII text characters. **It is best to turn on menu caching in order to speed up the display of the template**. The display is then nearly instantaneous.
|
||||
The 'status' view will persist showing until the API becomes available as without the API the custom template cannot be evaluated.
|
||||
|
||||
The default view will persist showing until the errors are resolved. In order to extract the custom glance template both the menu and the API are required. So it is logical that the two tests must pass first. The exception here is if the menu is cached, in which case only the API needs to pass.
|
||||
You may make this as complicated as you like! But you have limited space and only ASCII text characters. **It is essential to turn on menu caching in order to display of the template**. This is a change in v3.11 where multiple users are now making larger JSON menus than was originally envisaged for this watch application. As a result the Glance view would fail with an untrapable (fatal) _"Error: Out Of Memory Error"_. A work around is to pull out the glance subsection of the menu and cache that separately during execution of the full application, but that means any changes to the customised Glance view do not show until after the full application has been run.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Sadly what you cannot do is use special characters like: 🌞🔋⛅🪫. Whilst these do display in menu items, they do not seem to work on the Glance view. We really like them, so have tried but failed. Only ASCII text appears to be supported by the Garmin Connect IQ SDK's Glance View. This is not something we have any control over, please do not request this to be "fixed".
|
||||
> Sadly support for special characters like 🌞🔋⛅🪫 in the glance view is device dependent. For example they are not available on a Venu2 device but are available on a Venu X1 device. The application documentation shows them displaying in menu items. Only ASCII text appears to be universally supported by the Garmin Connect IQ (as you might hope). This is not something we have any control over, please do not request this to be "fixed".
|
||||
|
||||
It is possible to revert to the default glance content without deleting the template by changing the `type` to `status`.
|
||||
|
||||
@@ -68,10 +68,10 @@ So the glance view object has a `type` field with two possible values: `info` an
|
||||
|
||||
## Displayed Errors
|
||||
|
||||
The following shows the default glance when the menu file is not available at the specified URL.
|
||||
The following shows the status Glance view when the API not available at the specified URL.
|
||||
|
||||
<img src="../images/Venu2_glance_no_menu.png" width="200" title="Venu 2 Glance showing errors"/>
|
||||
<img src="../images/Venu2_glance_no_api.png" width="200" title="Venu 2 Glance showing errors"/>
|
||||
|
||||
Once the custom glance template has been retrieved and evaluated the display will change. Should the connectivity to your HomeAssistant then be lost, e.g. you move out of range of your phone, the glance reflects this in the colour of the residual two rectangles. The top one remains an indicator for the API, and the bottom rectangle remains an indicator for the menu availability, reflecting the original placement in the default glance view that has now been replaced.
|
||||
It is possible to loose connectivity with your HomeAssistant API after connecting and evaluating the Glance template. When the API connection is re-established, the Glance view will update.
|
||||
|
||||
<img src="../images/Venu2_glance_no_bt.png" width="200" title="Venu 2 Glance showing lost connectivity"/>
|
||||
|
||||
@@ -39,6 +39,36 @@ Field | Purpose
|
||||
|
||||
It may well be the case that often `attribute` and `data_attribute` are the same attribute, as with this example.
|
||||
|
||||
## Additional Action Data
|
||||
|
||||
The `tap_action` object may also include a `data` object for fixed service call parameters. When the picker value is selected, the application merges the picker value into this `data` object using `data_attribute`.
|
||||
|
||||
For example, a light brightness picker can request a HomeAssistant transition while still using the picker value for `brightness`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Lounge Brightness",
|
||||
"content": "{{ ((state_attr('light.lounge','brightness') | int(0) / 255) * 100) | round(0) }}%",
|
||||
"type": "numeric",
|
||||
"entity": "light.lounge",
|
||||
"tap_action": {
|
||||
"action": "light.turn_on",
|
||||
"data": {
|
||||
"transition": 2
|
||||
},
|
||||
"picker": {
|
||||
"step": 5,
|
||||
"min": 0,
|
||||
"max": 255,
|
||||
"attribute": "brightness",
|
||||
"data_attribute": "brightness"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The resulting action data sent to HomeAssistant contains both the fixed `transition` value and the selected `brightness` value.
|
||||
|
||||
## Helper
|
||||
|
||||
You might define a "helper" entity as follows in HomeAssistant:
|
||||
|
||||
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 15 KiB |
BIN
images/Venu2_glance2.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.2 KiB |
BIN
images/Venu2_glance_no_api.png
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
BIN
images/etiquette/2026-04-02_GHA_Bad_Review.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
@@ -43,6 +43,7 @@
|
||||
<iq:product id="d2deltas"/>
|
||||
<iq:product id="d2mach1"/>
|
||||
<iq:product id="d2mach2"/>
|
||||
<iq:product id="d2mach2pro"/>
|
||||
<iq:product id="descentg1"/>
|
||||
<iq:product id="descentg2"/>
|
||||
<iq:product id="descentmk2"/>
|
||||
@@ -101,6 +102,8 @@
|
||||
<iq:product id="fenixe"/>
|
||||
<iq:product id="fr165"/>
|
||||
<iq:product id="fr165m"/>
|
||||
<iq:product id="fr170"/>
|
||||
<iq:product id="fr170m"/>
|
||||
<iq:product id="fr245"/>
|
||||
<iq:product id="fr245m"/>
|
||||
<iq:product id="fr255"/>
|
||||
@@ -114,6 +117,7 @@
|
||||
<iq:product id="fr57047mm"/>
|
||||
<iq:product id="fr645"/>
|
||||
<iq:product id="fr645m"/>
|
||||
<iq:product id="fr70"/>
|
||||
<iq:product id="fr745"/>
|
||||
<iq:product id="fr935"/>
|
||||
<iq:product id="fr945"/>
|
||||
|
||||
@@ -52,12 +52,13 @@ d2deltas.resourcePath = $(d2deltas.resourcePath);resources-launcher-40-40;resour
|
||||
d2mach1.resourcePath = $(d2mach1.resourcePath);resources-launcher-60-60;resources-icons-48
|
||||
# Screen Size 454x454 launcher icon size 65x65
|
||||
d2mach2.resourcePath = $(d2mach2.resourcePath);resources-launcher-65-65;resources-icons-53
|
||||
d2mach2pro.resourcePath = $(d2mach2pro.resourcePath);resources-launcher-65-65;resources-icons-53
|
||||
# Screen Size 176x176 launcher icon size 62x62
|
||||
descentg1.resourcePath = $(descentg1.resourcePath);resources-launcher-62-62;resources-icons-21
|
||||
# Screen Size 390x390 launcher icon size 60x60
|
||||
descentg2.resourcePath = $(descentg2.resourcePath);resources-launcher-60-60;resources-icons-46
|
||||
# Screen Size 240x240 launcher icon size 40x40
|
||||
descentmk1.resourcePath = $(descentmk1.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
# Screen Size 240x240 launcher icon size 40x40 - Does not work in simulation
|
||||
#descentmk1.resourcePath = $(descentmk1.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
# Screen Size 280x280 launcher icon size 40x40
|
||||
descentmk2.resourcePath = $(descentmk2.resourcePath);resources-launcher-40-40;resources-icons-32
|
||||
# Screen Size 240x240 launcher icon size 40x40
|
||||
@@ -150,6 +151,8 @@ fenixe.resourcePath = $(fenixe.resourcePath);resources-launcher-60-60;resources-
|
||||
# Screen Size 390x390 launcher icon size 54x54
|
||||
fr165.resourcePath = $(descentmk2s.resourcePath);resources-launcher-54-54;resources-icons-46
|
||||
fr165m.resourcePath = $(descentmk2s.resourcePath);resources-launcher-54-54;resources-icons-46
|
||||
fr170.resourcePath = $(fr170.resourcePath);resources-launcher-54-54;resources-icons-46
|
||||
fr170m.resourcePath = $(fr170m.resourcePath);resources-launcher-54-54;resources-icons-46
|
||||
# Screen Size 240x240 launcher icon size 40x40
|
||||
fr245.resourcePath = $(fr245.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
fr245m.resourcePath = $(fr245m.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
@@ -171,6 +174,8 @@ fr57047mm.resourcePath = $(fr57047mm.resourcePath);resources-launcher-65-65;reso
|
||||
# Screen Size 240x240 launcher icon size 40x40
|
||||
fr645.resourcePath = $(fr645.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
fr645m.resourcePath = $(fr645m.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
# Screen Size 390x390 launcher icon size 54x54
|
||||
fr70.resourcePath = $(fr70.resourcePath);resources-launcher-54-54;resources-icons-46
|
||||
# Screen Size 240x240 launcher icon size 40x40
|
||||
fr745.resourcePath = $(fr745.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
fr935.resourcePath = $(fr935.resourcePath);resources-launcher-40-40;resources-icons-28
|
||||
|
||||
@@ -115,15 +115,18 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
|
||||
// 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) {
|
||||
if (position.accuracy != Position.QUALITY_NOT_AVAILABLE) {
|
||||
var accuracy = 0;
|
||||
switch (position.accuracy) {
|
||||
case Position.QUALITY_LAST_KNOWN:
|
||||
// Location is no longer tied to the timestamp, hence the larger inaccuracy.
|
||||
accuracy = 200;
|
||||
break;
|
||||
case Position.QUALITY_POOR:
|
||||
accuracy = 500;
|
||||
accuracy = 100;
|
||||
break;
|
||||
case Position.QUALITY_USABLE:
|
||||
accuracy = 100;
|
||||
accuracy = 50;
|
||||
break;
|
||||
case Position.QUALITY_GOOD:
|
||||
accuracy = 10;
|
||||
|
||||
@@ -27,11 +27,11 @@ using Toybox.Timer;
|
||||
//
|
||||
(:glance, :background)
|
||||
class HomeAssistantApp extends Application.AppBase {
|
||||
static const scStorageKeyMenu as Lang.String = "menu";
|
||||
static const scStorageKeyMenu as Lang.String = "menu";
|
||||
static const scStorageKeyGlance as Lang.String = "glance";
|
||||
|
||||
private var mHasToast as Lang.Boolean = false;
|
||||
private var mApiStatus as Lang.String?;
|
||||
private var mMenuStatus as Lang.String?;
|
||||
private var mHaMenu as HomeAssistantView?;
|
||||
private var mGlanceTemplate as Lang.String? = null;
|
||||
private var mGlanceText as Lang.String? = null;
|
||||
@@ -111,7 +111,6 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
mQuitTimer = new QuitTimer();
|
||||
mUpdateTimer = new Timer.Timer();
|
||||
mApiStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
|
||||
mHasToast = WatchUi has :showToast;
|
||||
Settings.update();
|
||||
|
||||
@@ -166,7 +165,6 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
// System.println("HomeAssistantApp onReturnFetchMenuConfig() Response Code: " + responseCode);
|
||||
// System.println("HomeAssistantApp onReturnFetchMenuConfig() Response Data: " + data);
|
||||
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String;
|
||||
switch (responseCode) {
|
||||
case Communications.BLE_HOST_TIMEOUT:
|
||||
case Communications.BLE_CONNECTION_UNAVAILABLE:
|
||||
@@ -205,12 +203,8 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
break;
|
||||
|
||||
case 200:
|
||||
if (data == null) {
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String;
|
||||
} else {
|
||||
if (hasCachedMenu()) {
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
|
||||
} else if (mIsApp) {
|
||||
if (data != null) {
|
||||
if (mIsApp) {
|
||||
// var stats = System.getSystemStats(); // stats.* values in bytes
|
||||
// System.println("HomeAssistantApp onReturnFetchMenuConfig() Memory: total=" + stats.totalMemory + ", used=" + stats.usedMemory + ", free=" + stats.freeMemory);
|
||||
|
||||
@@ -219,14 +213,14 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
// "Keys and values are limited to 8 KB each, and a total of 128 KB of storage is available."
|
||||
// "Storage.setValue() fails with an uncatchable out-of-memory error."
|
||||
Storage.setValue(scStorageKeyMenu, data as Lang.Dictionary);
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
|
||||
} else {
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Available) as Lang.String;
|
||||
// Store the smaller glance section of the menu separately so the Glance view can retrieve it within memory limits.
|
||||
var glance = (data as Lang.Dictionary)["glance"];
|
||||
if (glance != null) {
|
||||
Storage.setValue(scStorageKeyGlance, glance as Lang.Dictionary);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mIsApp) {
|
||||
glanceTemplate(data);
|
||||
} else {
|
||||
if (mIsApp) {
|
||||
if (data == null) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoJson) as Lang.String);
|
||||
} else if (data.size() == 0) {
|
||||
@@ -268,13 +262,13 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
function fetchMenuConfig() as Lang.Boolean {
|
||||
// System.println("Menu URL = " + Settings.getConfigUrl());
|
||||
if (Settings.getConfigUrl().equals("")) {
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Unconfigured) as Lang.String;
|
||||
WatchUi.requestUpdate();
|
||||
} else {
|
||||
var menu = Storage.getValue(scStorageKeyMenu) as Lang.Dictionary;
|
||||
if (menu != null and (Settings.getClearCache() || !Settings.getCacheConfig())) {
|
||||
// System.println("HomeAssistantApp fetchMenuConfig(): Clearing cached menu on user request.");
|
||||
Storage.deleteValue(scStorageKeyMenu);
|
||||
Storage.deleteValue(scStorageKeyGlance);
|
||||
menu = null;
|
||||
Settings.unsetClearCache();
|
||||
}
|
||||
@@ -282,13 +276,8 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
// System.println("HomeAssistantApp fetchMenuConfig(): Menu not cached, fetching.");
|
||||
fetchMenuConfigBasic(method(:onReturnFetchMenuConfig));
|
||||
} else {
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Cached) as Lang.String;
|
||||
WatchUi.requestUpdate();
|
||||
if (!mIsApp) {
|
||||
glanceTemplate(menu);
|
||||
} else {
|
||||
buildMenu(menu);
|
||||
}
|
||||
buildMenu(menu);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -329,7 +318,6 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
} else {
|
||||
ErrorView.show(WatchUi.loadResource(errorRez) as Lang.String);
|
||||
}
|
||||
mMenuStatus = WatchUi.loadResource(errorRez) as Lang.String;
|
||||
} else {
|
||||
Communications.makeWebRequest(
|
||||
Settings.getConfigUrl(),
|
||||
@@ -368,19 +356,17 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
|
||||
//! Extract the optional template to override the default glance view.
|
||||
//
|
||||
function glanceTemplate(menu as Lang.Dictionary) {
|
||||
if (menu != null) {
|
||||
if (menu["glance"] != null) {
|
||||
var glance = menu["glance"] as Lang.Dictionary;
|
||||
if (glance["type"].equals("info")) {
|
||||
mGlanceTemplate = glance["content"] as Lang.String;
|
||||
// System.println("HomeAssistantApp glanceTemplate() " + mGlanceTemplate);
|
||||
} else { // if glance["type"].equals("status")
|
||||
mGlanceTemplate = null;
|
||||
}
|
||||
function glanceTemplate() {
|
||||
var glance = Storage.getValue(scStorageKeyGlance) as Lang.Dictionary;
|
||||
if ((glance != null) && (glance["type"] != null)) {
|
||||
if (glance["type"].equals("info")) {
|
||||
mGlanceTemplate = glance["content"] as Lang.String;
|
||||
// System.println("HomeAssistantApp glanceTemplate() " + mGlanceTemplate);
|
||||
} else { // if glance["type"].equals("status")
|
||||
mGlanceTemplate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//! Test if two dictionaries are structurally equal. Used to see if the JSON menu has been
|
||||
//! amended but yet to be updated in the application cache.
|
||||
@@ -539,6 +525,11 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
if (menu == null || !structuralEquals(data, menu)) {
|
||||
// System.println("HomeAssistantApp onReturnCheckMenuConfig() New menu found.");
|
||||
Storage.setValue(scStorageKeyMenu, data as Lang.Dictionary);
|
||||
// Store the smaller glance section of the menu separately so the Glance view can retrieve it within memory limits.
|
||||
var glance = (data as Lang.Dictionary)["glance"];
|
||||
if (glance != null) {
|
||||
Storage.setValue(scStorageKeyGlance, glance as Lang.Dictionary);
|
||||
}
|
||||
if (menu != null) {
|
||||
// Notify the the user we have just got a newer menu file
|
||||
var toast = WatchUi.loadResource($.Rez.Strings.MenuUpdated) as Lang.String;
|
||||
@@ -897,51 +888,18 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
switch (responseCode) {
|
||||
case Communications.BLE_HOST_TIMEOUT:
|
||||
case Communications.BLE_CONNECTION_UNAVAILABLE:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent() Response Code: BLE_HOST_TIMEOUT or BLE_CONNECTION_UNAVAILABLE, Bluetooth connection severed.");
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoPhone) as Lang.String);
|
||||
}
|
||||
break;
|
||||
|
||||
case Communications.BLE_QUEUE_FULL:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent() Response Code: BLE_QUEUE_FULL, API calls too rapid.");
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.ApiFlood) as Lang.String);
|
||||
}
|
||||
break;
|
||||
|
||||
case Communications.NETWORK_REQUEST_TIMED_OUT:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent() Response Code: NETWORK_REQUEST_TIMED_OUT, check Internet connection.");
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoResponse) as Lang.String);
|
||||
}
|
||||
break;
|
||||
|
||||
case Communications.INVALID_HTTP_BODY_IN_NETWORK_RESPONSE:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent() Response Code: INVALID_HTTP_BODY_IN_NETWORK_RESPONSE, check JSON is returned.");
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoJson) as Lang.String);
|
||||
}
|
||||
break;
|
||||
|
||||
case 404:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent() Response Code: 404, page not found. Check Configuration URL setting.");
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.ConfigUrlNotFound) as Lang.String);
|
||||
}
|
||||
break;
|
||||
|
||||
case 200:
|
||||
if ((data != null) && (data instanceof Lang.Dictionary)) {
|
||||
mGlanceText = data["glanceTemplate"];
|
||||
}
|
||||
WatchUi.requestUpdate();
|
||||
break;
|
||||
|
||||
default:
|
||||
// System.println("HomeAssistantApp onReturnFetchGlanceContent(): Unhandled HTTP response code = " + responseCode);
|
||||
if (mIsApp) {
|
||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.UnhandledHttpErr) as Lang.String + responseCode);
|
||||
}
|
||||
}
|
||||
WatchUi.requestUpdate();
|
||||
}
|
||||
@@ -989,14 +947,6 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
return mApiStatus;
|
||||
}
|
||||
|
||||
//! Return the Menu status result.
|
||||
//!
|
||||
//! @return A string describing the Menu status
|
||||
//
|
||||
function getMenuStatus() as Lang.String {
|
||||
return mMenuStatus;
|
||||
}
|
||||
|
||||
//! Return the optional glance text that overrides the default glance content. This
|
||||
//! is derived from the glance template.
|
||||
//!
|
||||
@@ -1047,11 +997,12 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
function getGlanceView() as [ WatchUi.GlanceView ] or [ WatchUi.GlanceView, WatchUi.GlanceViewDelegate ] or Null {
|
||||
mIsApp = false; // A bit unnecessary given the default
|
||||
mApiStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
|
||||
mMenuStatus = WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String;
|
||||
Settings.update();
|
||||
glanceTemplate();
|
||||
updateStatus();
|
||||
mGlanceTimer = new Timer.Timer();
|
||||
mGlanceTimer.start(method(:updateStatus), Globals.scApiBackoffMs, true);
|
||||
// Although this is now known immediately, wait before displaying so the status can be seen first.
|
||||
return [new HomeAssistantGlanceView(self)];
|
||||
}
|
||||
|
||||
@@ -1066,8 +1017,6 @@ class HomeAssistantApp extends Application.AppBase {
|
||||
//! Update the menu and API statuses. Required for the Glance update timer.
|
||||
//
|
||||
function updateStatus() as Void {
|
||||
mGlanceTimer = null;
|
||||
fetchMenuConfig();
|
||||
fetchApiStatus();
|
||||
if (!Settings.getWebhookId().equals("") && !Settings.getClearWebhookId()) {
|
||||
fetchGlanceContent();
|
||||
|
||||
@@ -47,8 +47,6 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
private var mTitle as WatchUi.Text?;
|
||||
private var mApiText as WatchUi.Text?;
|
||||
private var mApiStatus as WatchUi.Text?;
|
||||
private var mMenuText as WatchUi.Text?;
|
||||
private var mMenuStatus as WatchUi.Text?;
|
||||
private var mGlanceContent as WatchUi.TextArea?;
|
||||
private var mAntiAlias as Lang.Boolean = false;
|
||||
|
||||
@@ -69,7 +67,7 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
function onLayout(dc as Graphics.Dc) as Void {
|
||||
var h = dc.getHeight();
|
||||
|
||||
mTextWidth = dc.getTextWidthInPixels(WatchUi.loadResource($.Rez.Strings.GlanceMenu) as Lang.String + ":", Graphics.FONT_XTINY);
|
||||
mTextWidth = dc.getTextWidthInPixels("API:", Graphics.FONT_XTINY);
|
||||
|
||||
mTitle = new WatchUi.Text({
|
||||
:text => WatchUi.loadResource($.Rez.Strings.AppName) as Lang.String,
|
||||
@@ -86,7 +84,7 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
:font => Graphics.FONT_XTINY,
|
||||
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
|
||||
:locX => scLeftRectMargin + scRectWidth + scRightRectMargin,
|
||||
:locY => 3 * h / 6
|
||||
:locY => 4 * h / 6
|
||||
});
|
||||
mApiStatus = new WatchUi.Text({
|
||||
:text => WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String,
|
||||
@@ -94,28 +92,11 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
:font => Graphics.FONT_XTINY,
|
||||
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
|
||||
:locX => scLeftRectMargin + scRectWidth + scRightRectMargin + scMidSep + mTextWidth,
|
||||
:locY => 3 * h / 6
|
||||
});
|
||||
|
||||
mMenuText = new WatchUi.Text({
|
||||
:text => WatchUi.loadResource($.Rez.Strings.GlanceMenu) as Lang.String + ":",
|
||||
:color => Graphics.COLOR_WHITE,
|
||||
:font => Graphics.FONT_XTINY,
|
||||
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
|
||||
:locX => scLeftRectMargin + scRectWidth + scRightRectMargin,
|
||||
:locY => 5 * h / 6
|
||||
});
|
||||
mMenuStatus = new WatchUi.Text({
|
||||
:text => WatchUi.loadResource($.Rez.Strings.Checking) as Lang.String,
|
||||
:color => Graphics.COLOR_WHITE,
|
||||
:font => Graphics.FONT_XTINY,
|
||||
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
|
||||
:locX => scLeftRectMargin + scRectWidth + scRightRectMargin + scMidSep + mTextWidth,
|
||||
:locY => 5 * h / 6
|
||||
:locY => 4 * h / 6
|
||||
});
|
||||
|
||||
mGlanceContent = new WatchUi.TextArea({
|
||||
:text => "A longer piece of text to wrap.",
|
||||
:text => "",
|
||||
:color => Graphics.COLOR_WHITE,
|
||||
:font => Graphics.FONT_XTINY,
|
||||
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
|
||||
@@ -134,10 +115,8 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
var h = dc.getHeight();
|
||||
var w = dc.getWidth() - scLeftRectMargin - scRightGlanceMargin;
|
||||
var apiStatus = mApp.getApiStatus();
|
||||
var menuStatus = mApp.getMenuStatus();
|
||||
var glanceText = mApp.getGlanceText();
|
||||
var apiCol;
|
||||
var menuCol;
|
||||
// System.println("HomeAssistantGlanceView onUpdate() glanceText=" + glanceText);
|
||||
|
||||
GlanceView.onUpdate(dc);
|
||||
@@ -160,33 +139,15 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
apiCol = Graphics.COLOR_RED;
|
||||
}
|
||||
|
||||
if (menuStatus.equals(WatchUi.loadResource($.Rez.Strings.Checking))) {
|
||||
menuCol = Graphics.COLOR_YELLOW;
|
||||
} else if (menuStatus.equals(WatchUi.loadResource($.Rez.Strings.Available))) {
|
||||
menuCol = Graphics.COLOR_GREEN;
|
||||
} else if (menuStatus.equals(WatchUi.loadResource($.Rez.Strings.Cached))) {
|
||||
menuCol = Graphics.COLOR_GREEN;
|
||||
} else {
|
||||
menuCol = Graphics.COLOR_RED;
|
||||
}
|
||||
|
||||
if (glanceText == null) {
|
||||
// Default Glance View
|
||||
// Status Glance View
|
||||
mApiText.draw(dc);
|
||||
mApiStatus.setText(apiStatus);
|
||||
mApiStatus.setColor(apiCol);
|
||||
dc.setColor(apiCol, apiCol);
|
||||
dc.drawRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, w, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, scRectWidth, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.drawRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, w, 4 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, scRectWidth, 4 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
mApiStatus.draw(dc);
|
||||
|
||||
mMenuText.draw(dc);
|
||||
mMenuStatus.setText(menuStatus);
|
||||
mMenuStatus.setColor(menuCol);
|
||||
dc.setColor(menuCol, menuCol);
|
||||
dc.drawRoundedRectangle(scLeftRectMargin, 4 * h / 6 + scVertMargin, w, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 4 * h / 6 + scVertMargin, scRectWidth, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
mMenuStatus.draw(dc);
|
||||
} else {
|
||||
// Customised Glance View
|
||||
dc.setColor(Graphics.COLOR_BLUE, Graphics.COLOR_BLUE);
|
||||
@@ -198,9 +159,7 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
|
||||
scRectRadius
|
||||
);
|
||||
dc.setColor(apiCol, apiCol);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, scRectWidth, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.setColor(menuCol, menuCol);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 4 * h / 6 + scVertMargin, scRectWidth, 2 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
dc.fillRoundedRectangle(scLeftRectMargin, 2 * h / 6 + scVertMargin, scRectWidth, 4 * h / 6 - (2 * scVertMargin), scRectRadius);
|
||||
mGlanceContent.setText(glanceText);
|
||||
mGlanceContent.draw(dc);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class HomeAssistantMenuItemFactory {
|
||||
label as Lang.String or Lang.Symbol,
|
||||
entity_id as Lang.String?,
|
||||
template as Lang.String?,
|
||||
action as Lang.String?,
|
||||
action as Lang.String?,
|
||||
data as Lang.Dictionary?,
|
||||
options as {
|
||||
:exit as Lang.Boolean,
|
||||
@@ -163,6 +163,7 @@ class HomeAssistantMenuItemFactory {
|
||||
entity_id as Lang.String?,
|
||||
template as Lang.String?,
|
||||
action as Lang.String?,
|
||||
data as Lang.Dictionary?,
|
||||
picker as Lang.Dictionary,
|
||||
options as {
|
||||
:exit as Lang.Boolean,
|
||||
@@ -171,9 +172,12 @@ class HomeAssistantMenuItemFactory {
|
||||
:icon as WatchUi.Bitmap
|
||||
}
|
||||
) as WatchUi.MenuItem {
|
||||
var data = null;
|
||||
if (entity_id != null) {
|
||||
data = { "entity_id" => entity_id };
|
||||
if (data == null) {
|
||||
data = { "entity_id" => entity_id };
|
||||
} else {
|
||||
data["entity_id"] = entity_id;
|
||||
}
|
||||
}
|
||||
var keys = mMenuItemOptions.keys();
|
||||
for (var i = 0; i < keys.size(); i++) {
|
||||
|
||||
@@ -155,7 +155,10 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
|
||||
//
|
||||
function onConfirm(b as Lang.Boolean) as Void {
|
||||
var dataAttribute = mPicker["data_attribute"] as Lang.String?;
|
||||
var entity_id = mData["entity_id"] as Lang.String?;
|
||||
var entity_id = null as Lang.String?;
|
||||
if (mData != null) {
|
||||
entity_id = mData["entity_id"] as Lang.String?;
|
||||
}
|
||||
|
||||
WatchUi.popView(WatchUi.SLIDE_RIGHT);
|
||||
WatchUi.requestUpdate();
|
||||
@@ -164,12 +167,18 @@ class HomeAssistantNumericMenuItem extends HomeAssistantMenuItem {
|
||||
return;
|
||||
}
|
||||
if (mAction != null) {
|
||||
var data = {} as Lang.Dictionary;
|
||||
if (mData != null) {
|
||||
var keys = mData.keys();
|
||||
for (var i = 0; i < keys.size(); i++) {
|
||||
data[keys[i]] = mData[keys[i]];
|
||||
}
|
||||
}
|
||||
data["entity_id"] = entity_id.toString();
|
||||
data[dataAttribute.toString()] = mValue;
|
||||
mHomeAssistantService.call(
|
||||
mAction,
|
||||
{
|
||||
"entity_id" => entity_id.toString(),
|
||||
dataAttribute.toString() => mValue
|
||||
},
|
||||
data,
|
||||
mExit
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,17 +30,20 @@ class HomeAssistantNumericPicker extends WatchUi.Picker {
|
||||
factory as HomeAssistantNumericFactory,
|
||||
haItem as HomeAssistantNumericMenuItem
|
||||
) {
|
||||
mItem = haItem;
|
||||
var picker = mItem.getPicker();
|
||||
var min = (picker.get("min") as Lang.String).toFloat();
|
||||
var step = (picker.get("step") as Lang.String).toFloat();
|
||||
var val = haItem.getValue();
|
||||
|
||||
if (min == null) {
|
||||
min = 0.0;
|
||||
mItem = haItem;
|
||||
var picker = mItem.getPicker();
|
||||
var minStr = picker.get("min");
|
||||
var stepStr = picker.get("step");
|
||||
var val = haItem.getValue();
|
||||
|
||||
var min = 0.0;
|
||||
var step = 1.0;
|
||||
|
||||
if (minStr != null) {
|
||||
min = (minStr as Lang.String).toFloat();
|
||||
}
|
||||
if (step == null) {
|
||||
step = 1.0;
|
||||
if (stepStr != null) {
|
||||
step = (stepStr as Lang.String).toFloat();
|
||||
}
|
||||
|
||||
WatchUi.Picker.initialize({
|
||||
@@ -95,4 +98,4 @@ class HomeAssistantNumericPickerDelegate extends WatchUi.PickerDelegate {
|
||||
mPicker.onConfirm(values[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,6 +122,7 @@ class HomeAssistantService {
|
||||
//!
|
||||
//! @param action The Home Assistant action to be run, e.g. from the JSON `action` field.
|
||||
//! @param data Data to be supplied to the action call.
|
||||
//! @param exit Whether to exit the app after making the call.
|
||||
//
|
||||
function call(
|
||||
action as Lang.String,
|
||||
|
||||
@@ -228,9 +228,7 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
||||
// If there's no response body, let's assume that what we did actually happened and flip the toggle.
|
||||
if (data.size() == 0) {
|
||||
setEnabled(!isEnabled());
|
||||
}
|
||||
|
||||
else {
|
||||
} else {
|
||||
for(var i = 0; i < data.size(); i++) {
|
||||
if ((data[i].get("entity_id") as Lang.String).equals(mData.get("entity_id"))) {
|
||||
var state = data[i].get("state") as Lang.String;
|
||||
|
||||
@@ -133,22 +133,39 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
||||
));
|
||||
}
|
||||
} else if (type.equals("numeric") && action != null) {
|
||||
if (tap_action != null) {
|
||||
var picker = tap_action.get("picker") as Lang.Dictionary?;
|
||||
if (picker != null) {
|
||||
addItem(HomeAssistantMenuItemFactory.create().numeric(
|
||||
name,
|
||||
entity,
|
||||
content,
|
||||
action,
|
||||
picker,
|
||||
{
|
||||
:exit => exit,
|
||||
:confirm => confirm,
|
||||
:pin => pin
|
||||
}
|
||||
));
|
||||
if (System.getDeviceSettings().isTouchScreen) {
|
||||
// Numeric items are only actionable on touch screen devices.
|
||||
if (tap_action != null) {
|
||||
var picker = tap_action.get("picker") as Lang.Dictionary?;
|
||||
if (picker != null) {
|
||||
addItem(HomeAssistantMenuItemFactory.create().numeric(
|
||||
name,
|
||||
entity,
|
||||
content,
|
||||
action,
|
||||
data,
|
||||
picker,
|
||||
{
|
||||
:exit => exit,
|
||||
:confirm => confirm,
|
||||
:pin => pin
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
addItem(HomeAssistantMenuItemFactory.create().tap(
|
||||
"PIN requires Touchscreen",
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
data,
|
||||
{
|
||||
:exit => false,
|
||||
:confirm => false,
|
||||
:pin => false
|
||||
}
|
||||
));
|
||||
}
|
||||
} else if (type.equals("info") && content != null) {
|
||||
// Cannot exit from a non-actionable information only menu item.
|
||||
@@ -192,11 +209,9 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
||||
}
|
||||
fullList.addAll(item.getMenuView().getItemsToUpdate());
|
||||
} else if (item instanceof HomeAssistantNumericMenuItem) {
|
||||
// Numeric items can have an optional template to evaluate
|
||||
var nmi = item as HomeAssistantNumericMenuItem;
|
||||
if (nmi.hasTemplate()) {
|
||||
fullList.add(item);
|
||||
}
|
||||
// Numeric items can have an optional template to evaluate, but they must always be included in order
|
||||
// to initialise the numeric picker's value.
|
||||
fullList.add(item);
|
||||
} else if (item instanceof HomeAssistantToggleMenuItem) {
|
||||
fullList.add(item);
|
||||
} else if (item instanceof HomeAssistantTapMenuItem) {
|
||||
|
||||