Cached 'has' result in initialize()

Also amended a bit of README that was missed and added a memory usage in the RootView for widgets here memory is limited, to serve as a warning to users who are likely to complain.
This commit is contained in:
Philip Abbey
2024-01-14 19:59:18 +00:00
parent 84aaa44995
commit 81762fbf7d
11 changed files with 92 additions and 22 deletions

View File

@ -276,8 +276,6 @@ The `id` attribute values are taken from the same names used in [`strings.xml`](
## Battery Level Reporting
We've had [a report](https://github.com/house-of-abbey/GarminHomeAssistant/issues/39) that this feature does not work without **administrator priviledges**. We've reviewed possible fixes and come up short. We are unable to fix this at present but invite those skilled in the art of Home Assistant to suggest a solution to us!
The application and widget both now include a background service to report your watch's battery level and charging status. This requires some [setup](BatteryReporting.md) via YAML in Home Assistant to display the transmitted value. We offer this [trouble shooting](Troubleshooting.md#watch-battery-level-reporting) guide.
## Version History
@ -296,6 +294,7 @@ The application and widget both now include a background service to report your
| 2.1 | Deployment of an idea to provide Home Assistant with access to the watch battery level. Using this requires [significant setup](BatteryReporting.md) on the Home Assistant configuration and hence is detailed separately. Due to this, the default state for this battery option is _off_. Changed the application settings user interface to be more intuitive, and hence amended the way settings are managed in the background. |
| 2.2 | Adds a feature to cache the menu configuration and save the time taken for an HTTP request to fetch it. You as the user are responsible for managing the cache by clearing it when you update your configuration. Improvement to widget root display updates. Bug fix for battery level reporting when in the glance carousel. Fixed an uninternationalised string, "Execute". Unfixed issue with battery level updates when the user is not an administrator. |
| 2.3 | Fix for battery level updates where previously the function only worked for administrator accounts. The new solution is based on Webhooks and is simpler to implement on Home Assistant. Language support fix where an automatic translation produced an inappropriate word, possibly in more than one language. |
| 2.4 | Sensor status reporting via Home Assistant 'templates'. This provides a generalised way of viewing the status of any entity as long as the result can be rendered as text, e.g. 'uncovered', 'open', '76%', '21 °C'. |
## Known Issues

View File

@ -36,6 +36,7 @@
<string id="Unconfigured" scope="glance">Unconfigured</string>
<string id="Cached" scope="glance">Cached</string>
<string id="GlanceMenu" scope="glance">Menu</string>
<string id="Memory" scope="glance">Memory</string>
<!-- For the settings GUI -->
<string id="SettingsSelect">Select...</string>

View File

@ -29,12 +29,13 @@ using Toybox.Timer;
class Alert extends WatchUi.View {
private static const bRadius = 10;
private var mTimer as Timer.Timer;
private var mTimeout as Lang.Number;
private var mText as Lang.String;
private var mFont as Graphics.FontType;
private var mFgcolor as Graphics.ColorType;
private var mBgcolor as Graphics.ColorType;
private var mTimer as Timer.Timer;
private var mTimeout as Lang.Number;
private var mText as Lang.String;
private var mFont as Graphics.FontType;
private var mFgcolor as Graphics.ColorType;
private var mBgcolor as Graphics.ColorType;
private var mAntiAlias as Lang.Boolean = false;
function initialize(params as Lang.Dictionary) {
View.initialize();
@ -64,6 +65,10 @@ class Alert extends WatchUi.View {
mTimeout = 2000;
}
if (Graphics.Dc has :setAntiAlias) {
mAntiAlias = true;
}
mTimer = new Timer.Timer();
}
@ -75,7 +80,7 @@ class Alert extends WatchUi.View {
mTimer.stop();
}
function onUpdate(dc) {
function onUpdate(dc as Graphics.Dc) {
var tWidth = dc.getTextWidthInPixels(mText, mFont);
var tHeight = dc.getFontHeight(mFont);
var bWidth = tWidth + 20;
@ -83,7 +88,7 @@ class Alert extends WatchUi.View {
var bX = (dc.getWidth() - bWidth) / 2;
var bY = (dc.getHeight() - bHeight) / 2;
if (dc has :setAntiAlias) {
if (mAntiAlias) {
dc.setAntiAlias(true);
}

View File

@ -44,7 +44,8 @@ class ErrorView extends ScalableView {
// Vertical spacing between the top of the face and the error icon
private var mErrorIconMargin as Lang.Number;
private var mErrorIcon;
private var mTextArea;
private var mTextArea as WatchUi.TextArea or Null;
private var mAntiAlias as Lang.Boolean = false;
private static var instance;
private static var mShown as Lang.Boolean = false;
@ -55,6 +56,9 @@ class ErrorView extends ScalableView {
// Convert the settings from % of screen size to pixels
mErrorIconMargin = pixelsForScreen(cSettings.get(:errorIconMargin) as Lang.Float);
mErrorIcon = Application.loadResource(Rez.Drawables.ErrorIcon) as Graphics.BitmapResource;
if (Graphics.Dc has :setAntiAlias) {
mAntiAlias = true;
}
}
// Load your resources here
@ -76,7 +80,7 @@ class ErrorView extends ScalableView {
// Update the view
function onUpdate(dc as Graphics.Dc) as Void {
var w = dc.getWidth();
if(dc has :setAntiAlias) {
if (mAntiAlias) {
dc.setAntiAlias(true);
}
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLUE);

View File

@ -33,4 +33,6 @@ class Globals {
// Needs to be long enough to enable a "double ESC" to quit the application from
// an ErrorView.
static const scApiResume = 200; // ms
// Warn the user after fetching the menu if their watch is low on memory before the device crashes.
static const scLowMem = 0.95; // percent as a fraction.
}

View File

@ -32,10 +32,14 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
private var mApiStatus as WatchUi.Text or Null;
private var mMenuText as WatchUi.Text or Null;
private var mMenuStatus as WatchUi.Text or Null;
private var mAntiAlias as Lang.Boolean = false;
function initialize(app as HomeAssistantApp) {
GlanceView.initialize();
mApp = app;
if (Graphics.Dc has :setAntiAlias) {
mAntiAlias = true;
}
}
function onLayout(dc as Graphics.Dc) as Void {
@ -85,9 +89,9 @@ class HomeAssistantGlanceView extends WatchUi.GlanceView {
});
}
function onUpdate(dc) as Void {
function onUpdate(dc as Graphics.Dc) as Void {
GlanceView.onUpdate(dc);
if(dc has :setAntiAlias) {
if(mAntiAlias) {
dc.setAntiAlias(true);
}
dc.setColor(

View File

@ -24,6 +24,17 @@ using Toybox.Graphics;
using Toybox.Application.Properties;
class HomeAssistantService {
private var mHasToast as Lang.Boolean = false;
private var mHasVibrate as Lang.Boolean = false;
function initialise() {
if (WatchUi has :showToast) {
mHasToast = true;
}
if (Attention has :vibrate) {
mHasVibrate = true;
}
}
// Callback function after completing the POST request to call a service.
//
@ -88,7 +99,7 @@ class HomeAssistantService {
toast = (d[i].get("attributes") as Lang.Dictionary).get("friendly_name") as Lang.String;
}
}
if (WatchUi has :showToast) {
if (mHasToast) {
WatchUi.showToast(toast, null);
} else {
new Alert({
@ -143,7 +154,7 @@ class HomeAssistantService {
},
method(:onReturnCall)
);
if (Attention has :vibrate) {
if (mHasVibrate) {
Attention.vibrate([
new Attention.VibeProfile(50, 100), // On for 100ms
new Attention.VibeProfile( 0, 100), // Off for 100ms

View File

@ -9,7 +9,7 @@
// tested on a Venu 2 device. The source code is provided at:
// https://github.com/house-of-abbey/GarminHomeAssistant.
//
// P A Abbey & J D Abbey & Someone0nEarth, 16 November 2023
// P A Abbey & J D Abbey & Someone0nEarth, 31 October 2023
//
//
// Description:

View File

@ -59,6 +59,7 @@ class RezStrings {
private static var strCached as Lang.String or Null;
(:glance)
private static var strGlanceMenu as Lang.String or Null;
private static var strMemory as Lang.String or Null;
// Can't initialise a constant directly, have to be initialised via a function
// for 'WatchUi.loadResource' to be available.
@ -103,6 +104,7 @@ class RezStrings {
strUnconfigured = WatchUi.loadResource($.Rez.Strings.Unconfigured);
strCached = WatchUi.loadResource($.Rez.Strings.Cached);
strGlanceMenu = WatchUi.loadResource($.Rez.Strings.GlanceMenu);
strMemory = WatchUi.loadResource($.Rez.Strings.Memory);
}
static function getAppName() as Lang.String {
@ -197,4 +199,8 @@ class RezStrings {
return strGlanceMenu;
}
static function getMemory() as Lang.String {
return strMemory;
}
}

View File

@ -21,6 +21,7 @@
using Toybox.Graphics;
using Toybox.Lang;
using Toybox.WatchUi;
using Toybox.System;
class RootView extends ScalableView {
@ -44,10 +45,16 @@ class RootView extends ScalableView {
private var mApiStatus as WatchUi.Text or Null;
private var mMenuText as WatchUi.Text or Null;
private var mMenuStatus as WatchUi.Text or Null;
private var mMemText as WatchUi.Text or Null;
private var mMemStatus as WatchUi.Text or Null;
private var mAntiAlias as Lang.Boolean = false;
function initialize(app as HomeAssistantApp) {
ScalableView.initialize();
mApp = app;
if (Graphics.Dc has :setAntiAlias) {
mAntiAlias = true;
}
}
function onLayout(dc as Graphics.Dc) as Void {
@ -84,9 +91,25 @@ class RootView extends ScalableView {
:font => Graphics.FONT_XTINY,
:justification => Graphics.TEXT_JUSTIFY_RIGHT | Graphics.TEXT_JUSTIFY_VCENTER,
:locX => w/2 - scMidSep/2,
:locY => pixelsForScreen(70.0)
:locY => pixelsForScreen(60.0)
});
mMenuStatus = new WatchUi.Text({
:text => RezStrings.getChecking(),
:color => Graphics.COLOR_WHITE,
:font => Graphics.FONT_XTINY,
:justification => Graphics.TEXT_JUSTIFY_LEFT | Graphics.TEXT_JUSTIFY_VCENTER,
:locX => w/2 + scMidSep/2,
:locY => pixelsForScreen(60.0)
});
mMemText = new WatchUi.Text({
:text => RezStrings.getMemory() + ":",
:color => Graphics.COLOR_WHITE,
:font => Graphics.FONT_XTINY,
:justification => Graphics.TEXT_JUSTIFY_RIGHT | Graphics.TEXT_JUSTIFY_VCENTER,
:locX => w/2 - scMidSep/2,
:locY => pixelsForScreen(70.0)
});
mMemStatus = new WatchUi.Text({
:text => RezStrings.getChecking(),
:color => Graphics.COLOR_WHITE,
:font => Graphics.FONT_XTINY,
@ -97,7 +120,7 @@ class RootView extends ScalableView {
}
function onUpdate(dc as Graphics.Dc) as Void {
if (dc has :setAntiAlias) {
if (mAntiAlias) {
dc.setAntiAlias(true);
}
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
@ -114,6 +137,16 @@ class RootView extends ScalableView {
mMenuText.draw(dc);
mMenuStatus.setText(mApp.getMenuStatus());
mMenuStatus.draw(dc);
mMemText.draw(dc);
var stats = System.getSystemStats();
var memUsed = (100 * stats.usedMemory) / stats.totalMemory;
mMemStatus.setText(memUsed.format("%.1f") + "%");
if (stats.usedMemory > Globals.scLowMem * stats.totalMemory) {
mMemStatus.setColor(Graphics.COLOR_RED);
} else {
mMemStatus.setColor(Graphics.COLOR_WHITE);
}
mMemStatus.draw(dc);
}
function onShow() as Void {

View File

@ -41,6 +41,7 @@ class Settings {
private static var mIsBatteryLevelEnabled as Lang.Boolean = false;
private static var mBatteryRefreshRate as Lang.Number = 15; // minutes
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.
private static var mWebhookManager as WebhookManager or Null;
@ -61,6 +62,10 @@ class Settings {
mIsBatteryLevelEnabled = Properties.getValue("enable_battery_level");
mBatteryRefreshRate = Properties.getValue("battery_level_refresh_rate");
if (System has :ServiceDelegate) {
mHasService = true;
}
// Manage this inside the application or widget only (not a glance or background service process)
if (mIsApp) {
if (mIsBatteryLevelEnabled) {
@ -68,14 +73,14 @@ class Settings {
mWebhookManager = new WebhookManager();
mWebhookManager.requestWebhookId();
} else if (
(System has :ServiceDelegate) and
mHasService and
((Background.getTemporalEventRegisteredTime() == null) or
(Background.getTemporalEventRegisteredTime() != (mBatteryRefreshRate * 60)))) {
Background.registerForTemporalEvent(new Time.Duration(mBatteryRefreshRate * 60)); // Convert to seconds
}
} else {
// Explicitly disable the background event which persists when the application closes.
if ((System has :ServiceDelegate) and (Background.getTemporalEventRegisteredTime() != null)) {
if (mHasService and (Background.getTemporalEventRegisteredTime() != null)) {
Background.deleteTemporalEvent();
}
unsetWebhookId();
@ -149,7 +154,7 @@ class Settings {
static function unsetIsBatteryLevelEnabled() {
mIsBatteryLevelEnabled = false;
Properties.setValue("enable_battery_level", mIsBatteryLevelEnabled);
if ((System has :ServiceDelegate) and (Background.getTemporalEventRegisteredTime() != null)) {
if (mHasService and (Background.getTemporalEventRegisteredTime() != null)) {
Background.deleteTemporalEvent();
}
}