Initial attempt

Lacking an end to end test on this code presently.
This commit is contained in:
Philip Abbey
2025-08-08 11:40:13 +01:00
parent 8a8d64bcab
commit c1cddc54e4
10 changed files with 78 additions and 24 deletions

View File

@ -94,6 +94,20 @@
-->
<property id="battery_level_refresh_rate" type="number">15</property>
<!--
A user specified HTTP header key to be used in all HTTP requests.
This is useful for some Home Assistant installations that require a
custom HTTP header.
-->
<property id="user_http_header_key" type="string"></property>
<!--
A user specified HTTP header value to be used in all HTTP requests.
This is useful for some Home Assistant installations that require a
custom HTTP header.
-->
<property id="user_http_header_value" type="string"></property>
<!--
The webhook ID is the last part of the webhook URL. It is secret and
should not be shared. It will not be set in settings but will be

View File

@ -119,6 +119,22 @@
<settingConfig type="numeric" min="5" />
</setting>
<group id="userHttpHeader" title="@Strings.SettingsUserHttpHeader" description="@Strings.SettingsUserHttpHeaderDescription">
<setting
propertyKey="@Properties.user_http_header_key"
title="@Strings.SettingsUserHttpHeaderKey"
>
<settingConfig type="alphaNumeric" />
</setting>
<setting
propertyKey="@Properties.user_http_header_value"
title="@Strings.SettingsUserHttpHeaderValue"
>
<settingConfig type="alphaNumeric" />
</setting>
</group>
<setting
propertyKey="@Properties.webhook_id"
title="@Strings.WebhookId"

View File

@ -57,6 +57,9 @@
<string id="SettingsConfigUrl">URL for menu configuration (JSON).</string>
<string id="SettingsCacheConfig">Should the application cache the menu configuration?</string>
<string id="SettingsClearCache">Should the application clear the existing cache next time it is started?</string>
<string id="WifiLteExecution">Wi-Fi/LTE execution mode.</string>
<string id="WifiLteExecutionEnable">Enable executing commands over Wi-Fi/LTE.</string>
<string id="WifiLteExecutionDescription">Allows the app to start without phone connection (when menu is cached), and prompt to execute command over Wi-Fi/LTE.</string>
<string id="SettingsVibration">Should the application provide feedback via vibrations?</string>
<string id="SettingsAppTimeout">Timeout in seconds. Exit the application after this period of inactivity to save the device battery.</string>
<string id="SettingsPollDelay">Additional poll delay (in seconds). Adds a delay between the status update of all menu items.</string>
@ -69,8 +72,9 @@
<string id="SettingsWidgetStart">(Widget only) Automatically start the application from the widget without waiting for a tap.</string>
<string id="SettingsEnableBatteryLevel">Enable the background service to send the device battery level, location and (if supported) activity data to Home Assistant.</string>
<string id="SettingsBatteryLevelRefreshRate">The refresh rate (in minutes) at which the background service should repeat sending data.</string>
<string id="SettingsUserHttpHeader">User supplied HTTP header</string>
<string id="SettingsUserHttpHeaderDescription">Some Home Assistant installations require the specification of a custom HTTP header in order to function.</string>
<string id="SettingsUserHttpHeaderKey">User supplied HTTP header key.</string>
<string id="SettingsUserHttpHeaderValue">User supplied HTTP header value.</string>
<string id="WebhookId">(Read only) The Webhook ID created by the device for background service updates. You might require this for debugging.</string>
<string id="WifiLteExecution">Wi-Fi/LTE execution mode.</string>
<string id="WifiLteExecutionEnable">Enable executing commands over Wi-Fi/LTE.</string>
<string id="WifiLteExecutionDescription">Allows the app to start without phone connection (when menu is cached), and prompt to execute command over Wi-Fi/LTE.</string>
</strings>

View File

@ -158,9 +158,9 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnDoUpdate)
@ -244,9 +244,9 @@ class BackgroundServiceDelegate extends System.ServiceDelegate {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnDoUpdate)

View File

@ -287,7 +287,8 @@ class HomeAssistantApp extends Application.AppBase {
null,
{
:method => Communications.HTTP_REQUEST_METHOD_GET,
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
:headers => Settings.augmentHttpHeaders({})
},
method(:onReturnFetchMenuConfig)
);
@ -501,9 +502,9 @@ class HomeAssistantApp extends Application.AppBase {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnUpdateMenuItems)
@ -619,9 +620,9 @@ class HomeAssistantApp extends Application.AppBase {
null,
{
:method => Communications.HTTP_REQUEST_METHOD_GET,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Authorization" => "Bearer " + Settings.getApiKey()
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnFetchApiStatus)
@ -714,9 +715,9 @@ class HomeAssistantApp extends Application.AppBase {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnFetchGlanceContent)

View File

@ -168,10 +168,10 @@ class HomeAssistantService {
data, // Includes {"entity_id": xxxx}
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON,
"Authorization" => "Bearer " + Settings.getApiKey()
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
:context => {
:entity_id => entity_id,

View File

@ -81,10 +81,10 @@ class HomeAssistantSyncDelegate extends Communications.SyncDelegate {
data, // May include {"entity_id": xxxx} for service calls
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON,
"Authorization" => "Bearer " + Settings.getApiKey()
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
},
method(:haCallback)

View File

@ -268,10 +268,10 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
mData,
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON,
"Authorization" => "Bearer " + Settings.getApiKey()
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnSetState)

View File

@ -47,6 +47,10 @@ class Settings {
private static var mIsSensorsLevelEnabled as Lang.Boolean = false;
//! minutes
private static var mBatteryRefreshRate as Lang.Number = 15;
//! Additional user configurable HTTP header key
private static var mUserHeaderKey as Lang.String = "";
//! Additional user configurable HTTP header value
private static var mUserHeaderValue as Lang.String = "";
private static var mIsApp as Lang.Boolean = false;
private static var mHasService as Lang.Boolean = false;
//! Must keep the object so it doesn't get garbage collected.
@ -71,6 +75,8 @@ class Settings {
mMenuAlignment = Properties.getValue("menu_alignment");
mIsSensorsLevelEnabled = Properties.getValue("enable_battery_level");
mBatteryRefreshRate = Properties.getValue("battery_level_refresh_rate");
mUserHeaderKey = Properties.getValue("user_http_header_key");
mUserHeaderValue = Properties.getValue("user_http_header_value");
}
//! A webhook is required for non-privileged API calls.
@ -284,4 +290,17 @@ class Settings {
}
}
//! Augment the HTTP header options passed in with the user configurable HTTP header key and value.
//!
//! @param options The HTTP header options to augment.
//!
//! @return The augmented HTTP header options.
//
static function augmentHttpHeaders(options as Lang.Dictionary) {
if (mUserHeaderKey != null && mUserHeaderKey != "" && mUserHeaderValue != null && mUserHeaderValue != "") {
options[mUserHeaderKey] = mUserHeaderValue;
}
return options;
}
}

View File

@ -113,10 +113,10 @@ class WebhookManager {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON,
"Authorization" => "Bearer " + Settings.getApiKey()
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
},
method(:onReturnRequestWebhookId)
@ -230,9 +230,9 @@ class WebhookManager {
},
{
:method => Communications.HTTP_REQUEST_METHOD_POST,
:headers => {
:headers => Settings.augmentHttpHeaders({
"Content-Type" => Communications.REQUEST_CONTENT_TYPE_JSON
},
}),
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON,
:context => sensors.slice(1, null)
},