mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-09-15 05:21:33 +00:00
Compare commits
5 Commits
ad7d278072
...
3d7b588d2c
Author | SHA1 | Date | |
---|---|---|---|
|
3d7b588d2c | ||
|
166b5f4ec3 | ||
|
57128bf7a4 | ||
|
64bebded0a | ||
|
b9db9af3bf |
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -4,6 +4,8 @@
|
|||||||
"Venu"
|
"Venu"
|
||||||
],
|
],
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"resources-*": true
|
"resources-*": true,
|
||||||
|
"bin": true,
|
||||||
|
"export": true
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# User Specified Custom HTTP Headers
|
# User Specified Custom HTTP Headers
|
||||||
|
|
||||||
Principally for those who use Home Assistant add-on [Cloudflared](https://github.com/brenner-tobias/addon-cloudflared) in order to provide additional security via Cloudflare's Web Application Firewall (WAF). But the solution is generic enough for other use cases.
|
Principally for those who use Home Assistant add-on [Cloudflared](https://github.com/brenner-tobias/addon-cloudflared) in order to provide additional security via Cloudflare's Web Application Firewall (WAF). But Garmin does not support certificates in requests. And the solution is generic enough for other use cases.
|
||||||
|
|
||||||
Please let us know if this solution is found to be useful for other situations.
|
Please let us know if this solution is found to be useful for other situations.
|
||||||
|
|
||||||
@@ -14,6 +14,12 @@ The settings contain two options for users to specify both the HTTP header name
|
|||||||
|
|
||||||
If you don't know why you need these, leave them empty and ignore.
|
If you don't know why you need these, leave them empty and ignore.
|
||||||
|
|
||||||
|
### Cloudflare WAF rule example
|
||||||
|
|
||||||
|
`(any(http.request.headers["your-header-name"][*] eq "your-header-key"))`
|
||||||
|
|
||||||
|
Make the key strong enough!
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
**None!**
|
**None!**
|
||||||
|
@@ -17,6 +17,8 @@ The application is designed around a simple scrollable menu where menu items hav
|
|||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> The Garmin SDK allows HTTP requests only to a limited number of domains specified in their app. Therefore, for your Garmin to communicate with your Home Assistant instance, your Home Assistant instance must be accessible via HTTPS (with a public certificate!) or through a local DNS server that overrides one of the whitelisted domains to communicate using HTTP.
|
> The Garmin SDK allows HTTP requests only to a limited number of domains specified in their app. Therefore, for your Garmin to communicate with your Home Assistant instance, your Home Assistant instance must be accessible via HTTPS (with a public certificate!) or through a local DNS server that overrides one of the whitelisted domains to communicate using HTTP.
|
||||||
>
|
>
|
||||||
|
>New with version 3.1, you can use [Cloudflared](https://github.com/brenner-tobias/addon-cloudflared) plug-in in combination with a [custom HTTP header](HTTP_Headers.md) and do not need a public certificate for HTTPS.
|
||||||
|
>
|
||||||
> To make your Home Assistant instance accessible via HTTPS, you will need a public certificate. You can get one for free from [Let's Encrypt](https://letsencrypt.org/) or you can pay for [Home Assistant cloud](https://www.nabucasa.com/). (You can install a local [Nginx proxy server](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a0d7b954_nginxproxymanager) to manage Let's Encrypt certificates.)
|
> To make your Home Assistant instance accessible via HTTPS, you will need a public certificate. You can get one for free from [Let's Encrypt](https://letsencrypt.org/) or you can pay for [Home Assistant cloud](https://www.nabucasa.com/). (You can install a local [Nginx proxy server](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a0d7b954_nginxproxymanager) to manage Let's Encrypt certificates.)
|
||||||
>
|
>
|
||||||
> If you use a local DNS server (like [Pi-Hole](https://pi-hole.net/)), you can create a local DNS record for the domain `garmincdn.com` (which is allowed for HTTP in the Garmin SDK) and map it to your Home Assistant instance's IP. "_[About Communication Between Garmin SDK and a Raspberry Pi](https://www.instructables.com/About-Communication-Between-Garmin-SDK-and-a-Raspb/)_" provides additional workarounds for HTTP request restrictions in the Garmin SDK.
|
> If you use a local DNS server (like [Pi-Hole](https://pi-hole.net/)), you can create a local DNS record for the domain `garmincdn.com` (which is allowed for HTTP in the Garmin SDK) and map it to your Home Assistant instance's IP. "_[About Communication Between Garmin SDK and a Raspberry Pi](https://www.instructables.com/About-Communication-Between-Garmin-SDK-and-a-Raspb/)_" provides additional workarounds for HTTP request restrictions in the Garmin SDK.
|
||||||
@@ -230,7 +232,7 @@ Make sure you can browse to the URL of your JSON file in a standard web browser
|
|||||||
|
|
||||||
## API Key Creation
|
## API Key Creation
|
||||||
|
|
||||||
Having created your JSON definition for your dashboard, you need to create an API key for your personal account on Home Assistant. You will need a [Long-Lived Access Token](https://developers.home-assistant.io/docs/auth_api/#long-lived-access-token). This is not obvious to find and is bound to your own Home Assistant account. Follow the menu sequence: `HA -> user profile -> Long-lived access tokens`. Make sure you save the generated token before dismissing it.
|
Having created your JSON definition for your dashboard, you need to create an API key for your personal account on Home Assistant. You will need a [Long-Lived Access Token](https://developers.home-assistant.io/docs/auth_api/#long-lived-access-token). This is not obvious to find and is bound to your own Home Assistant account. Follow the menu sequence: `HA -> User Profile -> "Security" tab -> Long-lived access tokens`. Make sure you save the generated token before dismissing it.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 634 KiB After Width: | Height: | Size: 557 KiB |
@@ -430,85 +430,87 @@ class HomeAssistantApp extends Application.AppBase {
|
|||||||
//! Construct the GET request to update all menu items.
|
//! Construct the GET request to update all menu items.
|
||||||
//
|
//
|
||||||
function updateMenuItems() as Void {
|
function updateMenuItems() as Void {
|
||||||
var phoneConnected = System.getDeviceSettings().phoneConnected;
|
if (mUpdating) {
|
||||||
var connectionAvailable = System.getDeviceSettings().connectionAvailable;
|
var phoneConnected = System.getDeviceSettings().phoneConnected;
|
||||||
|
var connectionAvailable = System.getDeviceSettings().connectionAvailable;
|
||||||
|
|
||||||
// In Wi-Fi/LTE execution mode, we should not show an error page but use a toast instead.
|
// In Wi-Fi/LTE execution mode, we should not show an error page but use a toast instead.
|
||||||
if (Settings.getWifiLteExecutionEnabled() && (! phoneConnected || ! connectionAvailable)) {
|
if (Settings.getWifiLteExecutionEnabled() && (! phoneConnected || ! connectionAvailable)) {
|
||||||
// Notify only once per disconnection cycle
|
// Notify only once per disconnection cycle
|
||||||
if (!mNotifiedNoBle) {
|
if (!mNotifiedNoBle) {
|
||||||
var toast = WatchUi.loadResource($.Rez.Strings.NoPhone);
|
var toast = WatchUi.loadResource($.Rez.Strings.NoPhone);
|
||||||
if (!connectionAvailable) {
|
if (!connectionAvailable) {
|
||||||
toast = WatchUi.loadResource($.Rez.Strings.NoInternet);
|
toast = WatchUi.loadResource($.Rez.Strings.NoInternet);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHasToast) {
|
||||||
|
WatchUi.showToast(toast, null);
|
||||||
|
} else {
|
||||||
|
new Alert({
|
||||||
|
:timeout => Globals.scAlertTimeoutMs,
|
||||||
|
:font => Graphics.FONT_MEDIUM,
|
||||||
|
:text => toast,
|
||||||
|
:fgcolor => Graphics.COLOR_WHITE,
|
||||||
|
:bgcolor => Graphics.COLOR_BLACK
|
||||||
|
}).pushView(WatchUi.SLIDE_IMMEDIATE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mHasToast) {
|
mNotifiedNoBle = true;
|
||||||
WatchUi.showToast(toast, null);
|
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
||||||
} else {
|
mUpdateTimer.start(method(:startUpdates), Globals.wifiPollResumeDelayMs, false);
|
||||||
new Alert({
|
|
||||||
:timeout => Globals.scAlertTimeoutMs,
|
mUpdating = false;
|
||||||
:font => Graphics.FONT_MEDIUM,
|
return;
|
||||||
:text => toast,
|
|
||||||
:fgcolor => Graphics.COLOR_WHITE,
|
|
||||||
:bgcolor => Graphics.COLOR_BLACK
|
|
||||||
}).pushView(WatchUi.SLIDE_IMMEDIATE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mNotifiedNoBle = true;
|
if (! phoneConnected) {
|
||||||
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
// System.println("HomeAssistantApp updateMenuItems(): No Phone connection, skipping API call.");
|
||||||
mUpdateTimer.start(method(:startUpdates), Globals.wifiPollResumeDelayMs, false);
|
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoPhone) as Lang.String);
|
||||||
|
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
||||||
|
} else if (! connectionAvailable) {
|
||||||
|
// System.println("HomeAssistantApp updateMenuItems(): No Internet connection, skipping API call.");
|
||||||
|
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoInternet) as Lang.String);
|
||||||
|
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
||||||
|
} else {
|
||||||
|
mNotifiedNoBle = false;
|
||||||
|
|
||||||
mUpdating = false;
|
if (mItemsToUpdate == null or mTemplates == null) {
|
||||||
return;
|
mItemsToUpdate = mHaMenu.getItemsToUpdate();
|
||||||
}
|
mTemplates = {};
|
||||||
|
for (var i = 0; i < mItemsToUpdate.size(); i++) {
|
||||||
if (! phoneConnected) {
|
var item = mItemsToUpdate[i];
|
||||||
// System.println("HomeAssistantApp updateMenuItems(): No Phone connection, skipping API call.");
|
var template = item.getTemplate();
|
||||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoPhone) as Lang.String);
|
if (template != null) {
|
||||||
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
mTemplates.put(i.toString(), {
|
||||||
} else if (! connectionAvailable) {
|
"template" => template
|
||||||
// System.println("HomeAssistantApp updateMenuItems(): No Internet connection, skipping API call.");
|
});
|
||||||
ErrorView.show(WatchUi.loadResource($.Rez.Strings.NoInternet) as Lang.String);
|
}
|
||||||
setApiStatus(WatchUi.loadResource($.Rez.Strings.Unavailable) as Lang.String);
|
if (item instanceof HomeAssistantToggleMenuItem) {
|
||||||
} else {
|
mTemplates.put(i.toString() + "t", {
|
||||||
mNotifiedNoBle = false;
|
"template" => (item as HomeAssistantToggleMenuItem).getToggleTemplate()
|
||||||
|
|
||||||
if (mItemsToUpdate == null or mTemplates == null) {
|
|
||||||
mItemsToUpdate = mHaMenu.getItemsToUpdate();
|
|
||||||
mTemplates = {};
|
|
||||||
for (var i = 0; i < mItemsToUpdate.size(); i++) {
|
|
||||||
var item = mItemsToUpdate[i];
|
|
||||||
var template = item.getTemplate();
|
|
||||||
if (template != null) {
|
|
||||||
mTemplates.put(i.toString(), {
|
|
||||||
"template" => template
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (item instanceof HomeAssistantToggleMenuItem) {
|
}
|
||||||
mTemplates.put(i.toString() + "t", {
|
|
||||||
"template" => (item as HomeAssistantToggleMenuItem).getToggleTemplate()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// https://developers.home-assistant.io/docs/api/native-app-integration/sending-data/#render-templates
|
||||||
|
// System.println("HomeAssistantApp updateMenuItems() URL=" + url + ", Template='" + mTemplate + "'");
|
||||||
|
Communications.makeWebRequest(
|
||||||
|
Settings.getApiUrl() + "/webhook/" + Settings.getWebhookId(),
|
||||||
|
{
|
||||||
|
"type" => "render_template",
|
||||||
|
"data" => mTemplates
|
||||||
|
},
|
||||||
|
{
|
||||||
|
:method => Communications.HTTP_REQUEST_METHOD_POST,
|
||||||
|
:headers => Settings.augmentHttpHeaders({
|
||||||
|
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
|
||||||
|
}),
|
||||||
|
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
||||||
|
},
|
||||||
|
method(:onReturnUpdateMenuItems)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// https://developers.home-assistant.io/docs/api/native-app-integration/sending-data/#render-templates
|
|
||||||
// System.println("HomeAssistantApp updateMenuItems() URL=" + url + ", Template='" + mTemplate + "'");
|
|
||||||
Communications.makeWebRequest(
|
|
||||||
Settings.getApiUrl() + "/webhook/" + Settings.getWebhookId(),
|
|
||||||
{
|
|
||||||
"type" => "render_template",
|
|
||||||
"data" => mTemplates
|
|
||||||
},
|
|
||||||
{
|
|
||||||
:method => Communications.HTTP_REQUEST_METHOD_POST,
|
|
||||||
:headers => Settings.augmentHttpHeaders({
|
|
||||||
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
|
|
||||||
}),
|
|
||||||
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
|
||||||
},
|
|
||||||
method(:onReturnUpdateMenuItems)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user