;
@@ -102,26 +106,35 @@ class HomeAssistantView extends WatchUi.Menu2 {
return fullList;
}
- // Called when this View is brought to the foreground. Restore
- // the state of this View and prepare it to be shown. This includes
- // loading resources into memory.
+ //! Called when this View is brought to the foreground. Restore
+ //! the state of this View and prepare it to be shown. This includes
+ //! loading resources into memory.
function onShow() as Void {}
}
-//
-// Reference: https://developer.garmin.com/connect-iq/core-topics/input-handling/
+//! Delegate for the HomeAssistantView.
+//!
+//! Reference: https://developer.garmin.com/connect-iq/core-topics/input-handling/
//
class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
private var mIsRootMenuView as Lang.Boolean = false;
private var mTimer as QuitTimer;
+ //! Class Constructor
+ //!
+ //! @param isRootMenuView As menus can be nested, this state marks the top level menu so that the
+ //! back event can exit the application completely rather than just popping
+ //! a menu view.
+ //
function initialize(isRootMenuView as Lang.Boolean) {
Menu2InputDelegate.initialize();
mIsRootMenuView = isRootMenuView;
mTimer = getApp().getQuitTimer();
}
+ //! Back button event
+ //
function onBack() {
mTimer.reset();
@@ -135,16 +148,22 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
WatchUi.popView(WatchUi.SLIDE_RIGHT);
}
- // Only for CheckboxMenu
+ //! Only for CheckboxMenu
+ //
function onDone() {
mTimer.reset();
}
- // Only for CustomMenu
+ //! Only for CustomMenu
+ //
function onFooter() {
mTimer.reset();
}
+ //! Select event
+ //!
+ //! @param item Selected menu item.
+ //
function onSelect(item as WatchUi.MenuItem) as Void {
mTimer.reset();
if (item instanceof HomeAssistantToggleMenuItem) {
@@ -164,7 +183,8 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
}
}
- // Only for CustomMenu
+ //! Only for CustomMenu
+ //
function onTitle() {
mTimer.reset();
}
diff --git a/source/QuitTimer.mc b/source/QuitTimer.mc
index a15efb0..7accd8d 100644
--- a/source/QuitTimer.mc
+++ b/source/QuitTimer.mc
@@ -11,11 +11,6 @@
//
// J D Abbey & P A Abbey, 28 December 2022
//
-//
-// Description:
-//
-// Quit the application after a period of inactivity in order to save the battery.
-//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
@@ -23,18 +18,27 @@ using Toybox.Timer;
using Toybox.Application.Properties;
using Toybox.WatchUi;
+//! Quit the application after a period of inactivity in order to save the battery.
+//!
class QuitTimer extends Timer.Timer {
+ //! Class Constructor
+ //
function initialize() {
Timer.Timer.initialize();
}
+ //! Can't see how to make a method object from `System.exit()` without this layer of
+ //! indirection. I assume this is because `System` is a static class.
+ //
function exitApp() as Void {
// System.println("QuitTimer exitApp(): Exiting");
// This will exit the system cleanly from any point within an app.
System.exit();
}
+ //! Kick off the quit timer.
+ //
function begin() {
var api_timeout = Settings.getAppTimeout(); // ms
if (api_timeout > 0) {
@@ -42,6 +46,8 @@ class QuitTimer extends Timer.Timer {
}
}
+ //! Reset the quit timer.
+ //
function reset() {
// System.println("QuitTimer reset(): Restarted quit timer");
stop();
diff --git a/source/ScalableView.mc b/source/ScalableView.mc
index 5d1a4bf..f381930 100644
--- a/source/ScalableView.mc
+++ b/source/ScalableView.mc
@@ -11,35 +11,36 @@
//
// J D Abbey & P A Abbey, 28 December 2022
//
-//
-// Description:
-//
-// A view with added methods to scale from percentages of scrren size to pixels.
-//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
using Toybox.WatchUi;
using Toybox.Math;
+//! A view that provides a common method 'pixelsForScreen' to make Views easier to layout on different
+//! sized watch screens.
+//
class ScalableView extends WatchUi.View {
+ //! Retain the local screen width for efficiency
private var mScreenWidth;
+ //! Class Constructor
+ //
function initialize() {
View.initialize();
mScreenWidth = System.getDeviceSettings().screenWidth;
}
- // Convert a fraction expressed as a percentage (%) to a number of pixels for the
- // screen's dimensions.
- //
- // Parameters:
- // * dc - Device context
- // * pc - Percentage (%) expressed as a number in the range 0.0..100.0
- //
- // Uses screen width rather than screen height as rectangular screens tend to have
- // height > width.
- //
+ //! Convert a fraction expressed as a percentage (%) to a number of pixels for the
+ //! screen's dimensions.
+ //!
+ //! Uses screen width rather than screen height as rectangular screens tend to have
+ //! height > width.
+ //!
+ //! @param pc Percentage (%) expressed as a number in the range 0.0..100.0
+ //!
+ //! @return Number of pixels for the screen's dimensions for a fraction expressed as a percentage (%).
+ //!
function pixelsForScreen(pc as Lang.Float) as Lang.Number {
return Math.round(pc * mScreenWidth) / 100;
}
diff --git a/source/Settings.mc b/source/Settings.mc
index 283c276..658c9a8 100644
--- a/source/Settings.mc
+++ b/source/Settings.mc
@@ -11,16 +11,6 @@
//
// P A Abbey & J D Abbey, SomeoneOnEarth & moesterheld, 23 November 2023
//
-//
-// Description:
-//
-// Home Assistant settings.
-//
-// WARNING!
-//
-// Careful putting ErrorView.show() calls in here. They need to be guarded so that
-// they do not get called when only displaying the glance view.
-//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
@@ -31,6 +21,11 @@ using Toybox.System;
using Toybox.Background;
using Toybox.Time;
+//! Home Assistant settings.
+//!
+//! WARNING! Careful putting ErrorView.show() calls in here. They need to be
+//! guarded so that they do not get called when only displaying the glance view.
+//
(:glance, :background)
class Settings {
private static var mApiKey as Lang.String = "";
@@ -40,19 +35,24 @@ class Settings {
private static var mCacheConfig as Lang.Boolean = false;
private static var mClearCache as Lang.Boolean = false;
private static var mVibrate as Lang.Boolean = false;
- private static var mAppTimeout as Lang.Number = 0; // seconds
- private static var mPollDelay as Lang.Number = 0; // seconds
- private static var mConfirmTimeout as Lang.Number = 3; // seconds
+ //! seconds
+ private static var mAppTimeout as Lang.Number = 0;
+ //! seconds
+ private static var mPollDelay as Lang.Number = 0;
+ //! seconds
+ private static var mConfirmTimeout as Lang.Number = 3;
private static var mPin as Lang.String or Null = "0000";
private static var mMenuAlignment as Lang.Number = WatchUi.MenuItem.MENU_ITEM_LABEL_ALIGN_LEFT;
private static var mIsSensorsLevelEnabled as Lang.Boolean = false;
- private static var mBatteryRefreshRate as Lang.Number = 15; // minutes
+ //! minutes
+ private static var mBatteryRefreshRate as Lang.Number = 15;
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.
+ //! Must keep the object so it doesn't get garbage collected.
private static var mWebhookManager as WebhookManager or Null;
- // Called on application start and then whenever the settings are changed.
+ //! Called on application start and then whenever the settings are changed.
+ //
static function update() {
mIsApp = getApp().getIsApp();
mApiKey = Properties.getValue("api_key");
@@ -71,6 +71,8 @@ class Settings {
mBatteryRefreshRate = Properties.getValue("battery_level_refresh_rate");
}
+ //! A webhook is required for non-privileged API calls.
+ //
static function webhook() {
if (System has :ServiceDelegate) {
mHasService = true;
@@ -116,65 +118,123 @@ class Settings {
// }
}
+ //! Get the API key supplied as part of the Settings.
+ //!
+ //! @return The API Key
+ //
static function getApiKey() as Lang.String {
return mApiKey;
}
+ //! Get the Webhook ID supplied as part of the Settings.
+ //!
+ //! @return The Webhook ID
+ //
static function getWebhookId() as Lang.String {
return mWebhookId;
}
+ //! Set the Webhook ID supplied as part of the Settings.
+ //!
+ //! @param webhookId The Webhook ID value to be saved.
+ //
static function setWebhookId(webhookId as Lang.String) {
mWebhookId = webhookId;
Properties.setValue("webhook_id", mWebhookId);
}
+ //! Delete the Webhook ID saved as part of the Settings.
+ //
static function unsetWebhookId() {
mWebhookId = "";
Properties.setValue("webhook_id", mWebhookId);
}
+ //! Get the API URL supplied as part of the Settings.
+ //!
+ //! @return The API URL
+ //
static function getApiUrl() as Lang.String {
return mApiUrl;
}
+ //! Get the menu configuration URL supplied as part of the Settings.
+ //!
+ //! @return The menu configuration URL
+ //
static function getConfigUrl() as Lang.String {
return mConfigUrl;
}
+ //! Get the menu cache Boolean option supplied as part of the Settings.
+ //!
+ //! @return Boolean for whether the menu should be cached to save application
+ //! start up time.
+ //
static function getCacheConfig() as Lang.Boolean {
return mCacheConfig;
}
+ //! Get the clear cache Boolean option supplied as part of the Settings.
+ //!
+ //! @return Boolean for whether the cache should be cleared next time the
+ //! application is started, forcing a menu refresh.
+ //
static function getClearCache() as Lang.Boolean {
return mClearCache;
}
+ //! Unset the clear cache Boolean option supplied as part of the Settings.
+ //
static function unsetClearCache() {
mClearCache = false;
Properties.setValue("clear_cache", mClearCache);
}
+ //! Get the vibration Boolean option supplied as part of the Settings.
+ //!
+ //! @return Boolean for whether vibration is enabled.
+ //
static function getVibrate() as Lang.Boolean {
return mVibrate;
}
+ //! Get the application timeout value supplied as part of the Settings.
+ //!
+ //! @return The application timeout in milliseconds.
+ //
static function getAppTimeout() as Lang.Number {
return mAppTimeout * 1000; // Convert to milliseconds
}
+ //! Get the application API polling interval supplied as part of the Settings.
+ //!
+ //! @return The application API polling interval in milliseconds.
+ //
static function getPollDelay() as Lang.Number {
return mPollDelay * 1000; // Convert to milliseconds
}
+ //! Get the menu item confirmation delay supplied as part of the Settings.
+ //!
+ //! @return The menu item confirmation delay in milliseconds.
+ //
static function getConfirmTimeout() as Lang.Number {
return mConfirmTimeout * 1000; // Convert to milliseconds
}
+ //! Get the menu item security PIN supplied as part of the Settings.
+ //!
+ //! @return The menu item security PIN.
+ //
static function getPin() as Lang.String or Null {
return mPin;
}
+ //! Check the user selected PIN confirms to 4 digits as a string.
+ //!
+ //! @return The validated 4 digit string.
+ //
private static function validatePin() as Lang.String or Null {
var pin = Properties.getValue("pin");
if (pin.toNumber() == null || pin.length() != 4) {
@@ -183,14 +243,24 @@ class Settings {
return pin;
}
+ //! Get the menu item alignment as part of the Settings.
+ //!
+ //! @return The menu item alignment.
+ //
static function getMenuAlignment() as Lang.Number {
return mMenuAlignment; // Either WatchUi.MenuItem.MENU_ITEM_LABEL_ALIGN_RIGHT or WatchUi.MenuItem.MENU_ITEM_LABEL_ALIGN_LEFT
}
+ //! Is logging of the watch sensors enabled? E.g. battery, activity etc.
+ //!
+ //! @return Boolean for whether logging of the watch sensors is enabled.
+ //
static function isSensorsLevelEnabled() as Lang.Boolean {
return mIsSensorsLevelEnabled;
}
+ //! Disable logging of the watch's sensors.
+ //
static function unsetIsSensorsLevelEnabled() {
mIsSensorsLevelEnabled = false;
Properties.setValue("enable_battery_level", mIsSensorsLevelEnabled);
diff --git a/source/WebLog.mc b/source/WebLog.mc
index 05dec93..53858f9 100644
--- a/source/WebLog.mc
+++ b/source/WebLog.mc
@@ -11,88 +11,99 @@
//
// J D Abbey & P A Abbey, 28 December 2022
//
-//
-// Description:
-//
-// WebLog provides a logging and hence debugging aid for when the application is
-// deployed to the watch. This is only used for development and use of it must not
-// persist into a deployed version. It uses a string buffer to group log entries into
-// larger submissions in order to prevent overflow of the blue tooth stack.
-//
//-----------------------------------------------------------------------------------
-//
-// Usage:
-// wl = new WebLog();
-// wl.clear();
-// wl.println("Debug Message");
-// wl.flush();
-//
-// https://domain.name/path/log.php
-//
-//
-//
-// Logs published to: https://domain.name/path/log
-//
-// https://domain.name/path/log_clear.php
-//
-//
using Toybox.Communications;
using Toybox.Lang;
using Toybox.System;
+//! WebLog provides a logging and hence debugging aid for when the application is
+//! deployed to the watch. This is only used for development and use of it must not
+//! persist into a deployed version. It uses a string buffer to group log entries into
+//! larger submissions in order to prevent overflow of the Bluetooth stack.
+//!
+//! Usage:
+//!
+//! wl = new WebLog();
+//! wl.clear();
+//! wl.println("Debug Message");
+//! wl.flush();
+//!
+//!
+//! File: https://domain.name/path/log.php
+//!
+//!
+//! <?php
+//! $myfile = fopen("log", "a");
+//! $queries = array();
+//! parse_str($_SERVER['QUERY_STRING'], $queries);
+//! fwrite($myfile, $queries['log']);
+//! print "Success";
+//! ?>
+//!
+//!
+//! Logs published to https://domain.name/path/log.
+//!
+//! File: https://domain.name/path/log_clear.php
+//!
+//!
+//! <?php
+//! $myfile = fopen("log", "w");
+//! fwrite($myfile, "");
+//! print "Success";
+//! ?>
+//!
+//
(:glance, :background)
class WebLog {
- private var callsbuffer = 4 as Lang.Number;
+ private var callsBuffer = 4 as Lang.Number;
private var numCalls = 0 as Lang.Number;
private var buffer = "" as Lang.String;
- // Set the number of calls to print() before sending the buffer to the online
- // logger.
+ //! Set the number of calls to print() before sending the buffer to the online
+ //! logger.
+ //!
+ //! @param l The number of log calls to buffer before writing to the online service.
//
function setCallsBuffer(l as Lang.Number) {
- callsbuffer = l;
+ callsBuffer = l;
}
- // Get the number of calls to print() before sending the buffer to the online
- // logger.
+ //! Get the number of calls to print() before sending the buffer to the online
+ //! logger.
+ //!
+ //! @return The number of log calls to buffer before writing to the online service.
//
function getCallsBuffer() as Lang.Number {
- return callsbuffer;
+ return callsBuffer;
}
- // Create a debug log over the Internet to keep track of the watch's runtime
- // execution.
+ //! Create a debug log over the Internet to keep track of the watch's runtime
+ //! execution.
+ //!
+ //! @param str The string to log.
//
function print(str as Lang.String) {
var myTime = System.getClockTime();
buffer += myTime.hour.format("%02d") + ":" + myTime.min.format("%02d") + ":" + myTime.sec.format("%02d") + " " + str;
numCalls++;
// System.println("WebLog print() str = " + str);
- if (numCalls >= callsbuffer) {
+ if (numCalls >= callsBuffer) {
doPrint();
}
}
- // Create a debug log over the Internet to keep track of the watch's runtime
- // execution. Add a new line character to the end.
+ //! Create a debug log over the Internet to keep track of the watch's runtime
+ //! execution. Add a new line character to the end.
+ //!
+ //! @param str The string to log.
//
function println(str as Lang.String) {
print(str + "\n");
}
- // Flush the current buffer to the online logger even if it has not reach the
- // submission level set by 'callsbuffer'.
+ //! Flush the current buffer to the online logger even if it has not reach the
+ //! submission level set by 'callsBuffer'.
//
function flush() {
// System.println("WebLog flush()");
@@ -101,7 +112,7 @@ class WebLog {
}
}
- // Perform the submission to the online logger.
+ //! Perform the submission to the online logger.
//
function doPrint() {
// System.println("WebLog doPrint()");
@@ -122,8 +133,8 @@ class WebLog {
buffer = "";
}
- // Clear the debug log over the Internet to start a new track of the watch's runtime
- // execution.
+ //! Clear the debug log over the Internet to start a new track of the watch's runtime
+ //! execution.
//
function clear() {
// System.println("WebLog clear()");
@@ -143,7 +154,10 @@ class WebLog {
buffer = "";
}
- // Callback function to print the outcome of a doPrint() method.
+ //! Callback function to print the outcome of a doPrint() method. Typically used for debugging this class.
+ //!
+ //! @param responseCode Response code.
+ //! @param data Response data.
//
function onLog(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
// if (responseCode != 200) {
@@ -153,7 +167,10 @@ class WebLog {
// }
}
- // Callback function to print the outcome of a clear() method.
+ // Callback function to print the outcome of a clear() method. Typically used for debugging this class.
+ //!
+ //! @param responseCode Response code.
+ //! @param data Response data.
//
function onClear(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
// if (responseCode != 200) {
diff --git a/source/WebhookManager.mc b/source/WebhookManager.mc
index c44ccec..07e0cbb 100644
--- a/source/WebhookManager.mc
+++ b/source/WebhookManager.mc
@@ -11,14 +11,6 @@
//
// P A Abbey & J D Abbey, 10 January 2024
//
-//
-// Description:
-//
-// Home Assistant Webhook creation.
-//
-// Reference:
-// * https://developers.home-assistant.io/docs/api/native-app-integration
-//
//-----------------------------------------------------------------------------------
using Toybox.Lang;
@@ -26,10 +18,24 @@ using Toybox.Communications;
using Toybox.System;
using Toybox.WatchUi;
-// Can use push view so must never be run in a glance context
+//! Home Assistant Webhook creation.
+//!
+//! NB. Because we can use push view (E.g. `ErrorView.show()`) this class must never
+//! be run in a glance context.
+//!
+//! Reference: https://developers.home-assistant.io/docs/api/native-app-integration
+//
class WebhookManager {
- function onReturnRequestWebhookId(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
+ //! Callback for requesting a Webhook ID.
+ //!
+ //! @param responseCode Response code
+ //! @param data Return data
+ //
+ function onReturnRequestWebhookId(
+ responseCode as Lang.Number,
+ data as Null or Lang.Dictionary or Lang.String
+ ) as Void {
switch (responseCode) {
case Communications.BLE_HOST_TIMEOUT:
case Communications.BLE_CONNECTION_UNAVAILABLE:
@@ -84,6 +90,8 @@ class WebhookManager {
}
}
+ //! Request a Webhook ID from Home Assistant for use in this application.
+ //
function requestWebhookId() {
var deviceSettings = System.getDeviceSettings();
// System.println("WebhookManager requestWebhookId(): Requesting webhook id for device = " + deviceSettings.uniqueIdentifier);
@@ -115,7 +123,18 @@ class WebhookManager {
);
}
- function onReturnRegisterWebhookSensor(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String, sensors as Lang.Array) as Void {
+ //! Callback function for the POST request to register the watch's sensors on the Home Assistant instance.
+ //!
+ //! @param responseCode Response code.
+ //! @param data Response data.
+ //! @param sensors The remaining sensors to be processed. The list of sensors is iterated through
+ //! until empty. Each POST request creating one sensor on the local Home Assistant.
+ //
+ function onReturnRegisterWebhookSensor(
+ responseCode as Lang.Number,
+ data as Null or Lang.Dictionary or Lang.String,
+ sensors as Lang.Array
+ ) as Void {
switch (responseCode) {
case Communications.BLE_HOST_TIMEOUT:
case Communications.BLE_CONNECTION_UNAVAILABLE:
@@ -194,7 +213,11 @@ class WebhookManager {
}
}
- function registerWebhookSensor(sensors as Lang.Array) {
+ //! Local method to send the POST request to register a number of sensors.
+ //!
+ //! @param sensors An array of sensors, e.g. As created by `registerWebhookSensors()`.
+ //
+ private function registerWebhookSensor(sensors as Lang.Array) {
var url = Settings.getApiUrl() + "/webhook/" + Settings.getWebhookId();
// System.println("WebhookManager registerWebhookSensor(): Registering webhook sensor: " + sensor.toString());
// System.println("WebhookManager registerWebhookSensor(): URL=" + url);
@@ -217,6 +240,8 @@ class WebhookManager {
);
}
+ //! Request the creation of all the supported watch sensors on the Home Assistant instance.
+ //
function registerWebhookSensors() {
var heartRate = Activity.getActivityInfo().currentHeartRate;