Skip to content

Commit 0298e68

Browse files
authored
Create lockscreen-hass-gauge.js
Simple Home Assistant (HASS) iOS Gauge Lock Screen Widget via Scriptable App
1 parent 1d8d1dd commit 0298e68

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed

source/lockscreen-hass-gauge.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Inspiration from https://github.com/awaescher/home-battery-widget/blob/main/widget.js
2+
// Simple Home Assistant (HASS) iOS Gauge Lock Screen Widget via Scriptable App
3+
4+
5+
const widget = new ListWidget();
6+
7+
// Get data from HASS
8+
let result = await loadValues();
9+
10+
// Check data quality
11+
let percent = result.value;
12+
let isValid = !Number.isNaN(percent);
13+
14+
if (!isValid) {
15+
percent = 0;
16+
}
17+
18+
// Build widget
19+
let progressStack = await progressCircle(widget, percent);
20+
21+
// Tiny house or triangle
22+
const mainIconName = isValid ? "house.fill" : "exclamationmark.triangle";
23+
24+
let mainIcon = SFSymbol.named(mainIconName);
25+
mainIcon.applyFont(Font.regularSystemFont(26));
26+
mainIcon = progressStack.addImage(mainIcon.image);
27+
const mainImageSize = 30;
28+
mainIcon.imageSize = new Size(mainImageSize, mainImageSize);
29+
mainIcon.tintColor = new Color("#fafafa");
30+
31+
// Tiny bolt (lightning strike) icon
32+
const badgeName = "bolt.fill";
33+
let badgeIcon = SFSymbol.named(badgeName);
34+
badgeIcon.applyFont(Font.regularSystemFont(26));
35+
badgeIcon = progressStack.addImage(badgeIcon.image);
36+
badgeIcon.imageSize = new Size(12, 12);
37+
badgeIcon.tintColor = new Color("#fafafa");
38+
39+
// iOS 16 gauge widget on lock screen
40+
widget.presentAccessoryCircular();
41+
// or classical widget? You need to decide.
42+
widget.backgroundColor = new Color("#7c7c7c", 1.0);
43+
//widget.presentSmall();
44+
45+
Script.setWidget(widget);
46+
Script.complete();
47+
48+
// color = "hsl(0, 0%, 100%)",
49+
async function progressCircle(
50+
on,
51+
value = 50,
52+
color = "hsl(0, 0%, 100%)",
53+
background = "hsl(0, 0%, 10%)",
54+
size = 60,
55+
barWidth = 5.5
56+
) {
57+
if (value > 1) {
58+
value /= 100
59+
}
60+
if (value < 0) {
61+
value = 0
62+
}
63+
if (value > 1) {
64+
value = 1
65+
}
66+
67+
// https://htmlcolors.com/hex-to-hsl
68+
if (value > 0.0 && value < 0.25) {
69+
color = "hsl(0, 100%, 50%)"; // red
70+
} else if (value >= 0.25 && value < 0.75) {
71+
color = "hsl(54, 100%, 50%)"; // yellow
72+
} else {
73+
color = "hsl(120, 100%, 50%)"; // green
74+
}
75+
76+
// Change colors in dark mode
77+
async function isUsingDarkAppearance() {
78+
return !Color.dynamic(Color.white(), Color.black()).red;
79+
}
80+
let isDark = await isUsingDarkAppearance();
81+
82+
if (color.split("-").length > 1) {
83+
if (isDark) {
84+
color = color.split("-")[1];
85+
} else {
86+
color = color.split("-")[0];
87+
}
88+
}
89+
90+
if (background.split("-").length > 1) {
91+
if (isDark) {
92+
background = background.split("-")[1];
93+
} else {
94+
background = background.split("-")[0];
95+
}
96+
}
97+
98+
let w = new WebView()
99+
await w.loadHTML('<canvas id="c"></canvas>');
100+
101+
// The magic gauge, filled with 'value'
102+
let base64 = await w.evaluateJavaScript(
103+
`let color = "${color}",
104+
background = "${background}",
105+
size = ${size}*3,
106+
lineWidth = ${barWidth}*3,
107+
percent = ${value * 100}
108+
let canvas = document.getElementById('c'),
109+
c = canvas.getContext('2d')
110+
canvas.width = size
111+
canvas.height = size
112+
let posX = canvas.width / 2,
113+
posY = canvas.height / 2,
114+
onePercent = 360 / 100,
115+
result = onePercent * percent
116+
c.lineCap = 'round'
117+
c.beginPath()
118+
c.arc( posX, posY, (size-lineWidth-1)/2, (Math.PI/180) * 270, (Math.PI/180) * (270 + 360) )
119+
c.strokeStyle = background
120+
c.lineWidth = lineWidth
121+
c.stroke()
122+
c.beginPath()
123+
c.strokeStyle = color
124+
c.lineWidth = lineWidth
125+
c.arc( posX, posY, (size-lineWidth-1)/2, (Math.PI/180) * 270, (Math.PI/180) * (270 + result) )
126+
c.stroke()
127+
completion(canvas.toDataURL().replace("data:image/png;base64,",""))`, true);
128+
const image = Image.fromData(Data.fromBase64String(base64));
129+
130+
// Add gauge to widget
131+
let stack = on.addStack();
132+
stack.size = new Size(size, size);
133+
stack.backgroundImage = image;
134+
stack.centerAlignContent();
135+
let padding = barWidth * 2;
136+
stack.setPadding(padding, padding, padding, padding);
137+
138+
return stack;
139+
}
140+
141+
// Get data from HASS
142+
async function loadValues() {
143+
144+
let req = new Request("https://<HASS IP>/api/states");
145+
req.headers = { "Authorization": "Bearer <HASS Long-Lived Access Token at https://<HASS IP>/profile", "content-type": "application/json" };
146+
let json = await req.loadJSON();
147+
148+
// Edit this, add your sensor
149+
let result = { name: 'sensor.your_sensor_of_interest', value: -1 };
150+
151+
// Search through HASS data find sensor and its current value
152+
var i;
153+
for (i = 0; i < json.length; i++) {
154+
if (json[i]['entity_id'] == result.name) {
155+
result.value = json[i]['state'];
156+
}
157+
}
158+
159+
// Testing / Debugging
160+
//result.value = 30;
161+
162+
console.log(result);
163+
return result;
164+
}

0 commit comments

Comments
 (0)