mirror of
https://github.com/house-of-abbey/GarminHomeAssistant.git
synced 2025-06-20 05:18:38 +00:00
Configurable Menu
Uses a JSON file served by a URL.
This commit is contained in:
15
manifest.xml
15
manifest.xml
@ -1,5 +1,18 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<!-- This is a generated file. It is highly recommended that you DO NOT edit this file. -->
|
<!--
|
||||||
|
|
||||||
|
Distributed under MIT Licence
|
||||||
|
See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
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, 31 October 2023
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq">
|
<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq">
|
||||||
<!--
|
<!--
|
||||||
Use "Monkey C: Edit Application" from the Visual Studio Code command palette
|
Use "Monkey C: Edit Application" from the Visual Studio Code command palette
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Distributed under MIT Licence
|
||||||
|
See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
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, 31 October 2023
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<drawables>
|
<drawables>
|
||||||
<bitmap id="LauncherIcon" filename="launcher_icon.png" />
|
<bitmap id="LauncherIcon" filename="launcher_icon.png" />
|
||||||
</drawables>
|
</drawables>
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<layout id="MainLayout">
|
|
||||||
<label x="center" y="5" text="@Strings.prompt" color="Graphics.COLOR_BLACK" justification="Graphics.TEXT_JUSTIFY_CENTER" />
|
|
||||||
<!--
|
|
||||||
<bitmap id="id_monkey" x="center" y="30" filename="../drawables/monkey.png" />
|
|
||||||
-->
|
|
||||||
</layout>
|
|
@ -1,4 +0,0 @@
|
|||||||
<menu id="MainMenu">
|
|
||||||
<menu-item id="item_1" label="@Strings.menu_label_1" />
|
|
||||||
<menu-item id="item_2" label="@Strings.menu_label_2" />
|
|
||||||
</menu>
|
|
@ -1,3 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Distributed under MIT Licence
|
||||||
|
See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
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, 31 October 2023
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<property id="api_key" type="string"></property>
|
<property id="api_key" type="string">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI1OTEzODI3NzhiNDI0MzU5OGVmYzY4ZmM2YzZiZTU3ZCIsImlhdCI6MTY5ODU3MDg0MywiZXhwIjoyMDEzOTMwODQzfQ.vskr0ERbnEXjB51vVHwlXosy3JK3v1znHzv8Hiej8zA</property>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Internal URL "https://homeassistant.local/api"
|
||||||
|
External URL "https://<dynamic DNS>/api"
|
||||||
|
-->
|
||||||
|
<property id="api_url" type="string">https://home.abbey1.org.uk/api</property>
|
||||||
|
|
||||||
|
<!-- Best be a public URL in order to work away from your home LAN and have a trusted HTTPS certificate -->
|
||||||
|
<property id="config_url" type="string">https://home.abbey1.org.uk/local/garmin/philip.json</property>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Distributed under MIT Licence
|
||||||
|
See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
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, 31 October 2023
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<settings>
|
<settings>
|
||||||
<setting
|
<setting
|
||||||
propertyKey="@Properties.api_key"
|
propertyKey="@Properties.api_key"
|
||||||
@ -8,4 +22,24 @@
|
|||||||
type="alphaNumeric"
|
type="alphaNumeric"
|
||||||
/>
|
/>
|
||||||
</setting>
|
</setting>
|
||||||
|
|
||||||
|
<setting
|
||||||
|
propertyKey="@Properties.api_url"
|
||||||
|
title="URL for Home Assistant API"
|
||||||
|
prompt="https://homeassistant.local/api"
|
||||||
|
>
|
||||||
|
<settingConfig
|
||||||
|
type="alphaNumeric"
|
||||||
|
/>
|
||||||
|
</setting>
|
||||||
|
|
||||||
|
<setting
|
||||||
|
propertyKey="@Properties.config_url"
|
||||||
|
title="URL for menu configuration (JSON)"
|
||||||
|
prompt="https://homeassistant.local/local/garmin/menu.json"
|
||||||
|
>
|
||||||
|
<settingConfig
|
||||||
|
type="alphaNumeric"
|
||||||
|
/>
|
||||||
|
</setting>
|
||||||
</settings>
|
</settings>
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Distributed under MIT Licence
|
||||||
|
See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
|
||||||
|
|
||||||
|
GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
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, 31 October 2023
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
<strings>
|
<strings>
|
||||||
<string id="AppName">HomeAssistant</string>
|
<string id="AppName">HomeAssistant</string>
|
||||||
|
|
||||||
<string id="prompt">Click the menu button</string>
|
|
||||||
|
|
||||||
<string id="menu_label_1">Item 1</string>
|
|
||||||
<string id="menu_label_2">Item 2</string>
|
|
||||||
</strings>
|
</strings>
|
||||||
|
142
source/Alert.mc
Normal file
142
source/Alert.mc
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// tested on a Venu 2 device. The source code is provided at:
|
||||||
|
// https://github.com/house-of-abbey/GarminHomeAssistant.
|
||||||
|
//
|
||||||
|
// J D Abbey & P A Abbey, 28 December 2022
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Alert provides a means to present application notifications to the user
|
||||||
|
// briefly. Credit to travis.vitek on forums.garmin.com.
|
||||||
|
//
|
||||||
|
// Reference:
|
||||||
|
// * https://forums.garmin.com/developer/connect-iq/f/discussion/106/how-to-show-alert-messages
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.Graphics;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
using Toybox.Timer;
|
||||||
|
|
||||||
|
const bRadius = 10;
|
||||||
|
|
||||||
|
class Alert extends WatchUi.View {
|
||||||
|
hidden var timer;
|
||||||
|
hidden var timeout;
|
||||||
|
hidden var text;
|
||||||
|
hidden var font;
|
||||||
|
hidden var fgcolor;
|
||||||
|
hidden var bgcolor;
|
||||||
|
|
||||||
|
function initialize(params as Lang.Dictionary) {
|
||||||
|
View.initialize();
|
||||||
|
|
||||||
|
text = params.get(:text);
|
||||||
|
if (text == null) {
|
||||||
|
text = "Alert";
|
||||||
|
}
|
||||||
|
|
||||||
|
font = params.get(:font);
|
||||||
|
if (font == null) {
|
||||||
|
font = Graphics.FONT_MEDIUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
fgcolor = params.get(:fgcolor);
|
||||||
|
if (fgcolor == null) {
|
||||||
|
fgcolor = Graphics.COLOR_BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bgcolor = params.get(:bgcolor);
|
||||||
|
if (bgcolor == null) {
|
||||||
|
bgcolor = Graphics.COLOR_WHITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = params.get(:timeout);
|
||||||
|
if (timeout == null) {
|
||||||
|
timeout = 2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = new Timer.Timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onShow() {
|
||||||
|
timer.start(method(:dismiss), timeout, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onHide() {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdate(dc) {
|
||||||
|
var tWidth = dc.getTextWidthInPixels(text, font);
|
||||||
|
var tHeight = dc.getFontHeight(font);
|
||||||
|
var bWidth = tWidth + 20;
|
||||||
|
var bHeight = tHeight + 15;
|
||||||
|
var bX = (dc.getWidth() - bWidth) / 2;
|
||||||
|
var bY = (dc.getHeight() - bHeight) / 2;
|
||||||
|
|
||||||
|
if(dc has :setAntiAlias) {
|
||||||
|
dc.setAntiAlias(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.setColor(
|
||||||
|
Graphics.COLOR_WHITE,
|
||||||
|
Graphics.COLOR_TRANSPARENT
|
||||||
|
);
|
||||||
|
dc.clear();
|
||||||
|
dc.setColor(bgcolor, bgcolor);
|
||||||
|
dc.fillRoundedRectangle(bX, bY, bWidth, bHeight, bRadius);
|
||||||
|
|
||||||
|
dc.setColor(fgcolor, bgcolor);
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
bX += i;
|
||||||
|
bY += i;
|
||||||
|
bWidth -= (2 * i);
|
||||||
|
bHeight -= (2 * i);
|
||||||
|
dc.drawRoundedRectangle(bX, bY, bWidth, bHeight, bRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tX = dc.getWidth() / 2;
|
||||||
|
var tY = bY + bHeight / 2;
|
||||||
|
dc.setColor(fgcolor, bgcolor);
|
||||||
|
dc.drawText(tX, tY, font, text, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the alert from view, usually on user input, but that is defined by the calling function.
|
||||||
|
//
|
||||||
|
function dismiss() {
|
||||||
|
WatchUi.popView(SLIDE_IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushView(transition) {
|
||||||
|
WatchUi.pushView(self, new AlertDelegate(self), transition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AlertDelegate extends WatchUi.InputDelegate {
|
||||||
|
hidden var mView;
|
||||||
|
|
||||||
|
function initialize(view) {
|
||||||
|
InputDelegate.initialize();
|
||||||
|
mView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKey(evt) {
|
||||||
|
mView.dismiss();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTap(evt) {
|
||||||
|
mView.dismiss();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,23 @@
|
|||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Home Assistant centralised constants.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
using Toybox.Lang;
|
using Toybox.Lang;
|
||||||
|
|
||||||
class Globals {
|
class Globals {
|
||||||
@ -5,11 +25,5 @@ class Globals {
|
|||||||
// as the messages can't be read from a watch!)
|
// as the messages can't be read from a watch!)
|
||||||
static const debug = false;
|
static const debug = false;
|
||||||
static const updateInterval = 5; // seconds
|
static const updateInterval = 5; // seconds
|
||||||
// static hidden const apiUrl = "https://homeassistant.local/api";
|
static const alertTimeout = 1000; // ms
|
||||||
static hidden const apiUrl = "https://home.abbey1.org.uk/api";
|
|
||||||
|
|
||||||
static function getApiUrl() {
|
|
||||||
return apiUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,108 @@
|
|||||||
import Toybox.Application;
|
//-----------------------------------------------------------------------------------
|
||||||
import Toybox.Lang;
|
//
|
||||||
import Toybox.WatchUi;
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Application root for GarminHomeAssistant.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Application;
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
using Toybox.Application.Properties;
|
||||||
|
using Toybox.Timer;
|
||||||
|
|
||||||
class HomeAssistantApp extends Application.AppBase {
|
class HomeAssistantApp extends Application.AppBase {
|
||||||
|
hidden var haMenu;
|
||||||
|
hidden var timer as Timer.Timer;
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
AppBase.initialize();
|
AppBase.initialize();
|
||||||
|
timer = new Timer.Timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
// onStart() is called on application start up
|
// onStart() is called on application start up
|
||||||
function onStart(state as Dictionary?) as Void {
|
function onStart(state as Lang.Dictionary?) as Void {
|
||||||
|
fetchMenuConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
// onStop() is called when your application is exiting
|
// onStop() is called when your application is exiting
|
||||||
function onStop(state as Dictionary?) as Void {
|
function onStop(state as Lang.Dictionary?) as Void {
|
||||||
|
if (timer != null) {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the initial view of your application here
|
// Return the initial view of your application here
|
||||||
function getInitialView() as Array<Views or InputDelegates>? {
|
function getInitialView() as Lang.Array<WatchUi.Views or WatchUi.InputDelegates>? {
|
||||||
return [ new HomeAssistantView(), new HomeAssistantViewDelegate() ] as Array<Views or InputDelegates>;
|
return [new WatchUi.View(), new WatchUi.BehaviorDelegate()] as Lang.Array<WatchUi.Views or WatchUi.InputDelegates>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback function after completing the GET request to fetch the configuration menu.
|
||||||
|
//
|
||||||
|
function onReturnFetchMenuConfig(responseCode as Lang.Number, data as Null or Lang.Dictionary or Lang.String) as Void {
|
||||||
|
if (Globals.debug) {
|
||||||
|
System.println("HomeAssistantApp onReturnFetchMenuConfig() Response Code: " + responseCode);
|
||||||
|
System.println("HomeAssistantApp onReturnFetchMenuConfig() Response Data: " + data);
|
||||||
|
}
|
||||||
|
if (responseCode == 200) {
|
||||||
|
haMenu = new HomeAssistantView(data, null);
|
||||||
|
timer.start(
|
||||||
|
haMenu.method(:stateUpdate),
|
||||||
|
Globals.updateInterval * 1000,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
WatchUi.switchToView(haMenu, new HomeAssistantViewDelegate(), WatchUi.SLIDE_IMMEDIATE);
|
||||||
|
} else {
|
||||||
|
if (Globals.debug) {
|
||||||
|
System.println("HomeAssistantApp Note - onReturnFetchMenuConfig(): Configuration not found or potential validation issue.");
|
||||||
|
}
|
||||||
|
new Alert({
|
||||||
|
:timeout => Globals.alertTimeout,
|
||||||
|
:font => Graphics.FONT_SYSTEM_MEDIUM,
|
||||||
|
:text => "Error " + responseCode,
|
||||||
|
:fgcolor => Graphics.COLOR_RED,
|
||||||
|
:bgcolor => Graphics.COLOR_BLACK
|
||||||
|
}).pushView(WatchUi.SLIDE_IMMEDIATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchMenuConfig() as Void {
|
||||||
|
var options = {
|
||||||
|
:method => Communications.HTTP_REQUEST_METHOD_GET,
|
||||||
|
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
||||||
|
};
|
||||||
|
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
||||||
|
Communications.makeWebRequest(
|
||||||
|
Properties.getValue("config_url"),
|
||||||
|
null,
|
||||||
|
options,
|
||||||
|
method(:onReturnFetchMenuConfig)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (Globals.debug) {
|
||||||
|
System.println("HomeAssistantApp Note - fetchMenuConfig(): No Internet connection, skipping API call.");
|
||||||
|
}
|
||||||
|
new Alert({
|
||||||
|
:timeout => Globals.alertTimeout,
|
||||||
|
:font => Graphics.FONT_SYSTEM_MEDIUM,
|
||||||
|
:text => "No Internet connection",
|
||||||
|
:fgcolor => Graphics.COLOR_RED,
|
||||||
|
:bgcolor => Graphics.COLOR_BLACK
|
||||||
|
}).pushView(WatchUi.SLIDE_IMMEDIATE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,26 @@
|
|||||||
import Toybox.Lang;
|
//-----------------------------------------------------------------------------------
|
||||||
import Toybox.WatchUi;
|
//
|
||||||
import Toybox.Graphics;
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Menu button that triggers a script.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
using Toybox.Graphics;
|
||||||
using Toybox.Application.Properties;
|
using Toybox.Application.Properties;
|
||||||
|
|
||||||
class HomeAssistantMenuItem extends WatchUi.MenuItem {
|
class HomeAssistantMenuItem extends WatchUi.MenuItem {
|
||||||
@ -11,7 +31,7 @@ class HomeAssistantMenuItem extends WatchUi.MenuItem {
|
|||||||
subLabel as Lang.String or Lang.Symbol or Null,
|
subLabel as Lang.String or Lang.Symbol or Null,
|
||||||
identifier as Lang.Object or Null,
|
identifier as Lang.Object or Null,
|
||||||
options as {
|
options as {
|
||||||
:alignment as MenuItem.Alignment,
|
:alignment as WatchUi.MenuItem.Alignment,
|
||||||
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
|
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
|
||||||
} or Null
|
} or Null
|
||||||
) {
|
) {
|
||||||
@ -51,7 +71,7 @@ class HomeAssistantMenuItem extends WatchUi.MenuItem {
|
|||||||
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
||||||
};
|
};
|
||||||
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
||||||
var url = Globals.getApiUrl() + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/" + mIdentifier.substring(mIdentifier.find(".")+1, null);
|
var url = Properties.getValue("api_url") + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/" + mIdentifier.substring(mIdentifier.find(".")+1, null);
|
||||||
if (Globals.debug) {
|
if (Globals.debug) {
|
||||||
System.println("URL=" + url);
|
System.println("URL=" + url);
|
||||||
System.println("mIdentifier=" + mIdentifier);
|
System.println("mIdentifier=" + mIdentifier);
|
||||||
|
@ -1,14 +1,32 @@
|
|||||||
import Toybox.Lang;
|
//-----------------------------------------------------------------------------------
|
||||||
import Toybox.WatchUi;
|
//
|
||||||
import Toybox.Graphics;
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Light or switch toggle button that calls the API to maintain the up to date state.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
using Toybox.Graphics;
|
||||||
using Toybox.Application.Properties;
|
using Toybox.Application.Properties;
|
||||||
|
|
||||||
class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
||||||
hidden var api_key = Properties.getValue("api_key");
|
hidden var api_key = Properties.getValue("api_key");
|
||||||
hidden var menu;
|
|
||||||
|
|
||||||
function initialize(
|
function initialize(
|
||||||
menu as HomeAssistantView,
|
|
||||||
label as Lang.String or Lang.Symbol,
|
label as Lang.String or Lang.Symbol,
|
||||||
subLabel as Lang.String or Lang.Symbol or {
|
subLabel as Lang.String or Lang.Symbol or {
|
||||||
:enabled as Lang.String or Lang.Symbol or Null,
|
:enabled as Lang.String or Lang.Symbol or Null,
|
||||||
@ -17,25 +35,25 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
|||||||
identifier,
|
identifier,
|
||||||
enabled as Lang.Boolean,
|
enabled as Lang.Boolean,
|
||||||
options as {
|
options as {
|
||||||
:alignment as MenuItem.Alignment,
|
:alignment as WatchUi.MenuItem.Alignment,
|
||||||
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
|
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol
|
||||||
} or Null
|
} or Null
|
||||||
) {
|
) {
|
||||||
WatchUi.ToggleMenuItem.initialize(label, subLabel, identifier, enabled, options);
|
WatchUi.ToggleMenuItem.initialize(label, subLabel, identifier, enabled, options);
|
||||||
api_key = Properties.getValue("api_key");
|
api_key = Properties.getValue("api_key");
|
||||||
self.menu = menu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setUiToggle(state as Null or Lang.String) as Void {
|
private function setUiToggle(state as Null or Lang.String) as Void {
|
||||||
if (state != null) {
|
if (state != null) {
|
||||||
if (state.equals("on") && !isEnabled()) {
|
if (state.equals("on") && !isEnabled()) {
|
||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
|
WatchUi.requestUpdate();
|
||||||
} else if (state.equals("off") && isEnabled()) {
|
} else if (state.equals("off") && isEnabled()) {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
}
|
|
||||||
WatchUi.requestUpdate();
|
WatchUi.requestUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Callback function after completing the GET request to fetch the status.
|
// Callback function after completing the GET request to fetch the status.
|
||||||
//
|
//
|
||||||
@ -65,7 +83,7 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
|||||||
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
:responseType => Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON
|
||||||
};
|
};
|
||||||
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
||||||
var url = Globals.getApiUrl() + "/states/" + mIdentifier;
|
var url = Properties.getValue("api_url") + "/states/" + mIdentifier;
|
||||||
if (Globals.debug) {
|
if (Globals.debug) {
|
||||||
System.println("URL=" + url);
|
System.println("URL=" + url);
|
||||||
}
|
}
|
||||||
@ -116,9 +134,9 @@ class HomeAssistantToggleMenuItem extends WatchUi.ToggleMenuItem {
|
|||||||
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
if (System.getDeviceSettings().phoneConnected && System.getDeviceSettings().connectionAvailable) {
|
||||||
var url;
|
var url;
|
||||||
if (s) {
|
if (s) {
|
||||||
url = Globals.getApiUrl() + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/turn_on";
|
url = Properties.getValue("api_url") + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/turn_on";
|
||||||
} else {
|
} else {
|
||||||
url = Globals.getApiUrl() + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/turn_off";
|
url = Properties.getValue("api_url") + "/services/" + mIdentifier.substring(0, mIdentifier.find(".")) + "/turn_off";
|
||||||
}
|
}
|
||||||
if (Globals.debug) {
|
if (Globals.debug) {
|
||||||
System.println("URL=" + url);
|
System.println("URL=" + url);
|
||||||
|
@ -1,93 +1,89 @@
|
|||||||
import Toybox.Lang;
|
//-----------------------------------------------------------------------------------
|
||||||
import Toybox.Graphics;
|
//
|
||||||
import Toybox.WatchUi;
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Home Assistant menu construction.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.Graphics;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
|
||||||
class HomeAssistantView extends WatchUi.Menu2 {
|
class HomeAssistantView extends WatchUi.Menu2 {
|
||||||
hidden var timer;
|
|
||||||
|
|
||||||
function initialize() {
|
|
||||||
timer = new Timer.Timer();
|
|
||||||
|
|
||||||
|
function initialize(
|
||||||
|
definition as Lang.Dictionary,
|
||||||
|
options as {
|
||||||
|
:focus as Lang.Number,
|
||||||
|
:icon as Graphics.BitmapType or WatchUi.Drawable or Lang.Symbol,
|
||||||
|
:theme as WatchUi.MenuTheme or Null
|
||||||
|
} or Null
|
||||||
|
) {
|
||||||
var toggle_obj = {
|
var toggle_obj = {
|
||||||
:enabled => "On",
|
:enabled => "On",
|
||||||
:disabled => "Off"
|
:disabled => "Off"
|
||||||
};
|
};
|
||||||
|
|
||||||
WatchUi.Menu2.initialize({
|
if (options == null) {
|
||||||
:title => "Entities"
|
options = {
|
||||||
});
|
:title => definition.get("title") as Lang.String
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
options.put(:title, definition.get("title") as Lang.String);
|
||||||
|
}
|
||||||
|
WatchUi.Menu2.initialize(options);
|
||||||
|
|
||||||
|
var items = definition.get("items") as Lang.Dictionary;
|
||||||
|
for(var i = 0; i < items.size(); i++) {
|
||||||
|
var type = items[i].get("type") as Lang.String or Null;
|
||||||
|
var name = items[i].get("name") as Lang.String or Null;
|
||||||
|
var entity = items[i].get("entity") as Lang.String or Null;
|
||||||
|
if (type != null && name != null && entity != null) {
|
||||||
|
if (type.equals("toggle")) {
|
||||||
addItem(
|
addItem(
|
||||||
new HomeAssistantToggleMenuItem(
|
new HomeAssistantToggleMenuItem(
|
||||||
self,
|
name,
|
||||||
"Bedroom Light",
|
|
||||||
toggle_obj,
|
toggle_obj,
|
||||||
"light.philip_s_bedside_light_switch",
|
entity,
|
||||||
false,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
addItem(
|
|
||||||
new HomeAssistantToggleMenuItem(
|
|
||||||
self,
|
|
||||||
"Lounge Lights",
|
|
||||||
toggle_obj,
|
|
||||||
"light.living_room_ambient_lights_all",
|
|
||||||
false,
|
false,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
} else if (type.equals("tap")) {
|
||||||
addItem(
|
addItem(
|
||||||
new HomeAssistantMenuItem(
|
new HomeAssistantMenuItem(
|
||||||
"Food is Ready!",
|
name,
|
||||||
null,
|
"Tap",
|
||||||
"script.food_is_ready",
|
entity,
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// addItem(
|
} else if (type.equals("group")) {
|
||||||
// new HomeAssistantMenuItem(
|
|
||||||
// "Test Script",
|
|
||||||
// null,
|
|
||||||
// "script.test",
|
|
||||||
// null
|
|
||||||
// )
|
|
||||||
// );
|
|
||||||
addItem(
|
addItem(
|
||||||
new HomeAssistantToggleMenuItem(
|
new HomeAssistantViewMenuItem(items[i])
|
||||||
self,
|
|
||||||
"Bookcase USBs",
|
|
||||||
toggle_obj,
|
|
||||||
"switch.bookcase_usbs",
|
|
||||||
false,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
addItem(
|
|
||||||
new HomeAssistantToggleMenuItem(
|
|
||||||
self,
|
|
||||||
"Corner Table USBs",
|
|
||||||
toggle_obj,
|
|
||||||
"switch.corner_table_usbs",
|
|
||||||
false,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Load your resources here
|
}
|
||||||
function onLayout(dc as Dc) as Void {
|
|
||||||
setLayout(Rez.Layouts.MainLayout(dc));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when this View is brought to the foreground. Restore
|
// Called when this View is brought to the foreground. Restore
|
||||||
// the state of this View and prepare it to be shown. This includes
|
// the state of this View and prepare it to be shown. This includes
|
||||||
// loading resources into memory.
|
// loading resources into memory.
|
||||||
function onShow() as Void {
|
function onShow() as Void {
|
||||||
timer.start(
|
|
||||||
method(:timerUpdate),
|
|
||||||
Globals.updateInterval * 1000,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
for(var i = 0; i < mItems.size(); i++) {
|
for(var i = 0; i < mItems.size(); i++) {
|
||||||
if (mItems[i] instanceof HomeAssistantToggleMenuItem) {
|
if (mItems[i] instanceof HomeAssistantToggleMenuItem) {
|
||||||
var toggleItem = mItems[i] as HomeAssistantToggleMenuItem;
|
var toggleItem = mItems[i] as HomeAssistantToggleMenuItem;
|
||||||
@ -99,26 +95,20 @@ class HomeAssistantView extends WatchUi.Menu2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the view
|
function stateUpdate() as Void {
|
||||||
function onUpdate(dc as Dc) as Void {
|
|
||||||
View.onUpdate(dc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when this View is removed from the screen. Save the
|
|
||||||
// state of this View here. This includes freeing resources from
|
|
||||||
// memory.
|
|
||||||
function onHide() as Void {
|
|
||||||
timer.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function timerUpdate() as Void {
|
|
||||||
for(var i = 0; i < mItems.size(); i++) {
|
for(var i = 0; i < mItems.size(); i++) {
|
||||||
if (mItems[i] instanceof HomeAssistantToggleMenuItem) {
|
if (mItems[i] instanceof HomeAssistantToggleMenuItem) {
|
||||||
var toggleItem = mItems[i] as HomeAssistantToggleMenuItem;
|
var toggleItem = mItems[i] as HomeAssistantToggleMenuItem;
|
||||||
toggleItem.getState();
|
toggleItem.getState();
|
||||||
if (Globals.debug) {
|
if (Globals.debug) {
|
||||||
System.println("HomeAssistantView Note: " + toggleItem.getLabel() + " ID=" + toggleItem.getId() + " Enabled=" + toggleItem.isEnabled());
|
System.println("HomeAssistantView Toggle stateUpdate: " + toggleItem.getLabel() + " ID=" + toggleItem.getId() + " Enabled=" + toggleItem.isEnabled());
|
||||||
}
|
}
|
||||||
|
} else if (mItems[i] instanceof HomeAssistantViewMenuItem) {
|
||||||
|
var menu = mItems[i] as HomeAssistantViewMenuItem;
|
||||||
|
if (Globals.debug) {
|
||||||
|
System.println("HomeAssistantView Menu stateUpdate: " + menu.getLabel() + " ID=" + menu.getId());
|
||||||
|
}
|
||||||
|
menu.getMenuView().stateUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,6 +134,13 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
System.println(haItem.getLabel() + " " + haItem.getId());
|
System.println(haItem.getLabel() + " " + haItem.getId());
|
||||||
}
|
}
|
||||||
haItem.execScript();
|
haItem.execScript();
|
||||||
|
} else if (item instanceof HomeAssistantViewMenuItem) {
|
||||||
|
var haMenuItem = item as HomeAssistantViewMenuItem;
|
||||||
|
if (Globals.debug) {
|
||||||
|
System.println("Menu: " + haMenuItem.getLabel() + " " + haMenuItem.getId());
|
||||||
|
}
|
||||||
|
// No delegate state to be amended, so re-use 'self'.
|
||||||
|
WatchUi.pushView(haMenuItem.getMenuView(), self, WatchUi.SLIDE_LEFT);
|
||||||
} else {
|
} else {
|
||||||
if (Globals.debug) {
|
if (Globals.debug) {
|
||||||
System.println(item.getLabel() + " " + item.getId());
|
System.println(item.getLabel() + " " + item.getId());
|
||||||
@ -151,4 +148,16 @@ class HomeAssistantViewDelegate extends WatchUi.Menu2InputDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSwipe(swipeEvent) as Lang.Boolean {
|
||||||
|
switch (swipeEvent.getDirection()) {
|
||||||
|
case WatchUi.SWIPE_RIGHT:
|
||||||
|
WatchUi.popView(WatchUi.SLIDE_RIGHT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
43
source/HomeAssistantViewMenuItem.mc
Normal file
43
source/HomeAssistantViewMenuItem.mc
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Distributed under MIT Licence
|
||||||
|
// See https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/LICENSE.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// GarminHomeAssistant is a Garmin IQ application written in Monkey C and routinely
|
||||||
|
// 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, 31 October 2023
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
// Menu button that opens a sub-menu.
|
||||||
|
//
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using Toybox.Lang;
|
||||||
|
using Toybox.WatchUi;
|
||||||
|
|
||||||
|
class HomeAssistantViewMenuItem extends WatchUi.MenuItem {
|
||||||
|
hidden var menu as HomeAssistantView;
|
||||||
|
|
||||||
|
function initialize(definition as Lang.Dictionary) {
|
||||||
|
// definitions.get(...) are Strings here as they have been checked by HomeAssistantView first
|
||||||
|
WatchUi.MenuItem.initialize(
|
||||||
|
definition.get("name") as Lang.String,
|
||||||
|
"Menu",
|
||||||
|
definition.get("entity") as Lang.String,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
menu = new HomeAssistantView(definition, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMenuView() as HomeAssistantView {
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user