Skip to content

Instantly share code, notes, and snippets.

@malosaa
Forked from m33x/gauge.js
Last active July 13, 2023 17:30
Show Gist options
  • Save malosaa/c2611c8e490d6e0af1b5366577b5e2ba to your computer and use it in GitHub Desktop.
Save malosaa/c2611c8e490d6e0af1b5366577b5e2ba to your computer and use it in GitHub Desktop.
Simple Home Assistant (HASS) iOS Gauge Lock Screen Widget via Scriptable App
// produce test `
const widget = new ListWidget();
// Get data from HASS
let result = await loadValues();
// Check data quality
let value = result.value;
let isValid = !Number.isNaN(value);
if (!isValid) {
value = 0;
}
// Build widget
let progressStack = await progressCircle(widget, value);
// Tiny house or triangle
const mainIconName = isValid ? "house.fill" : "exclamationmark.triangle";
const mainIconSize = 30;
let mainStack = progressStack.addStack();
mainStack.layoutVertically();
mainStack.centerAlignContent();
let mainIcon = SFSymbol.named(mainIconName);
mainIcon.applyFont(Font.regularSystemFont(mainIconSize));
mainIcon = mainStack.addImage(mainIcon.image);
mainIcon.imageSize = new Size(mainIconSize, mainIconSize);
mainIcon.tintColor = new Color("#fafafa");
widget.addSpacer(); // Add spacer to push the valueStack to the right
let valueStack = widget.addStack();
valueStack.layoutVertically();
valueStack.centerAlignContent();
let valueLabel = mainStack.addText(`${result.value}W`);
valueLabel.font = Font.mediumSystemFont(14);
valueLabel.textColor = new Color("#fafafa");
valueLabel.textOpacity = 0.8;
valueLabel.leftAlignText();
valueLabel.minimumScaleFactor = 0.5;
valueLabel.lineLimit = 1;
// Tiny bolt (lightning strike) icon
const badgeName = "bolt.fill";
let badgeIcon = SFSymbol.named(badgeName);
badgeIcon.applyFont(Font.regularSystemFont(26));
badgeIcon = progressStack.addImage(badgeIcon.image);
badgeIcon.imageSize = new Size(12, 12);
badgeIcon.tintColor = new Color("#fafafa");
// iOS 16 gauge widget on lock screen
widget.presentAccessoryCircular();
// or classical widget? You need to decide.
widget.backgroundColor = new Color("#7c7c7c", 1.0);
//widget.presentSmall();
Script.setWidget(widget);
Script.complete();
async function progressCircle(
on,
value = 50,
size = 60,
background = "hsl(1200, 0%, 10%)",
barWidth = 5.5
) {
if (value > 1000) {
color = "hsl(120, 100%, 50%)"; // green
} else if (value >= 500 && value <= 1000) {
color = "hsl(54, 100%, 50%)"; // yellow
} else {
color = "hsl(0, 100%, 50%)"; // red
}
// Change colors in dark mode
async function isUsingDarkAppearance() {
return !Color.dynamic(Color.white(), Color.black()).red;
}
let isDark = await isUsingDarkAppearance();
let background = isDark ? "hsl(0, 0%, 10%)" : "hsl(0, 0%, 100%)";
let w = new WebView()
await w.loadHTML('<canvas id="c"></canvas>');
// The magic gauge, filled with 'value'
let base64 = await w.evaluateJavaScript(`
let color = "${color}",
background = "${background}",
size = ${size}*3,
lineWidth = ${barWidth}*3,
percent = ${value * 100};
let canvas = document.getElementById('c'),
c = canvas.getContext('2d');
canvas.width = size;
canvas.height = size;
let posX = canvas.width / 2,
posY = canvas.height / 2,
onePercent = 360 / 100,
result = onePercent * percent;
c.lineCap = 'round';
c.beginPath();
c.arc(
posX,
posY,
(size - lineWidth - 1) / 2,
(Math.PI / 180) * 270,
(Math.PI / 180) * (270 + 360)
);
c.strokeStyle = background;
c.lineWidth = lineWidth;
c.stroke();
c.beginPath();
c.strokeStyle = color;
c.lineWidth = lineWidth;
c.arc(
posX,
posY,
(size - lineWidth - 1) / 2,
(Math.PI / 180) * 270,
(Math.PI / 180) * (270 + result)
);
c.stroke();
completion(canvas.toDataURL().replace("data:image/png;base64,", ""));
`);
const image = Image.fromData(Data.fromBase64String(base64));
// Add gauge to widget
let stack = on.addStack();
stack.size = new Size(size, size);
stack.backgroundImage = image;
stack.centerAlignContent();
let padding = barWidth * 2;
stack.setPadding(padding, padding, padding, padding);
return stack;
}
async function loadValues() {
// Replace the URL and headers with your HASS details
let req = new Request("https://<HASS IP>/api/states");
req.headers = {
"Authorization": "Bearer <HASS Long-Lived Access Token at https://<HASS IP>/profile>",
"content-type": "application/json"
};
let json = await req.loadJSON();
// Edit this, add your sensor
let result = { name: 'sensor.your_sensor_of_interest', value: -1 };
// Search through HASS data find sensor and its current value
for (let i = 0; i < json.length; i++) {
if (json[i]['entity_id'] === result.name) {
result.value = parseFloat(json[i]['state']);
break;
}
}
// Testing / Debugging
//result.value = 30;
console.log(result);
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment