create your own plausible analytics ios widget
Here’s a script to create your own Plausible Analytics iOS widget. With this 1:1 block widget, you can access key daily metrics directly from your iPhone or iPad’s Home Screen, such as real-time data, page views, unique visitors, top referrer, and top country. See set-up instructions below. Applicable to self-hosted instances.
The script:
// Scriptable iOS widget for self-hosted Plausible instances
// By mgx.me. Inspired by @linuz90 Fabrizio Rinaldi
// Change date filters according to your preferences
// replace analytics.example.com with your baseURL where plausible is hosted
let baseURL = 'https://analytics.example.com/api/v1/stats/'
// Replace PlausibleToken with your API Key
let head = {"Authorization":"Bearer verynicetoken"}
// Replace example.com with your siteID, i.e, domain.tld
let siteID = 'example.com'
// Get the data to draw chart based on daily metrics
const url = baseURL + 'timeseries?site_id=' + siteID + '&period=day'
let req = await new Request(url)
req.headers = head
var result = await req.loadJSON()
// Get realtime visitor metrics
const url2 = baseURL + 'realtime/visitors?site_id=' + siteID
let req2 = await new Request(url2)
req2.headers = head
const liveVisitors = await req2.loadString()
var copy1 = '● ' + await req2.loadString() + ' online'
// Get daily active users and pageviews
const url3 = baseURL + 'aggregate?site_id=' + siteID + '&period=day&metrics=visitors,pageviews'
let req3 = await new Request(url3)
req3.headers = head
var result2 = await req3.loadJSON()
// Top traffic source today
const url4 = baseURL + 'breakdown?site_id=' + siteID + '&period=day&property=visit:source&limit=1'
let req4 = await new Request(url4)
req4.headers = head
var result3 = await req4.loadJSON()
// Top country source today
const url5 = baseURL + 'breakdown?site_id=' + siteID + '&period=day&property=visit:country&metrics=visitors&limit=1'
let req5 = await new Request(url5)
req5.headers = head
var result4 = await req5.loadJSON()
// LineChart
class LineChart {
// LineChart by https://kevinkub.de/
constructor(width, height, values) {
this.ctx = new DrawContext();
this.ctx.size = new Size(width, height);
this.values = values;
}
_calculatePath() {
let maxValue = Math.max(...this.values);
let minValue = Math.min(...this.values);
let difference = maxValue - minValue;
let count = this.values.length;
let step = this.ctx.size.width / (count - 1);
let points = this.values.map((current, index, all) => {
let x = step*index;
let y = this.ctx.size.height - (current - minValue) / difference * this.ctx.size.height;
return new Point(x, y);
});
return this._getSmoothPath(points);
}
_getSmoothPath(points) {
let path = new Path();
path.move(new Point(0, this.ctx.size.height));
path.addLine(points[0]);
for(let i = 0; i < points.length-1; i++) {
let xAvg = (points[i].x + points[i+1].x) / 2;
let yAvg = (points[i].y + points[i+1].y) / 2;
let avg = new Point(xAvg, yAvg);
let cp1 = new Point((xAvg + points[i].x) / 2, points[i].y);
let next = new Point(points[i+1].x, points[i+1].y);
let cp2 = new Point((xAvg + points[i+1].x) / 2, points[i+1].y);
path.addQuadCurve(avg, cp1);
path.addQuadCurve(next, cp2);
}
path.addLine(new Point(this.ctx.size.width, this.ctx.size.height));
path.closeSubpath();
return path;
}
configure(fn) {
let path = this._calculatePath();
if(fn) {
fn(this.ctx, path);
} else {
this.ctx.addPath(path);
this.ctx.fillPath(path);
}
return this.ctx;
}
}
// Widget for iOS
let widget = new ListWidget()
widget.backgroundColor = new Color("#252F3F")
const txtColor = new Color("#E5E7EB")
const onlineColor = new Color("#1abc9c")
// Stack icon and brand name (get SF Symbols or use an image)
let sym = SFSymbol.named("cursor.rays")
let brandStack = widget.addStack()
let icon = brandStack.addImage(sym.image)
icon.tintColor = txtColor
icon.imageSize = new Size(15, 15)
let textStack = brandStack.addStack()
let domain = textStack.addText(' Example Daily')
domain.textColor = txtColor
domain.font = Font.boldSystemFont(13);
brandStack.layoutHorizontally()
// Webs page to visit when you tap on the widget
widget.url = 'https://analytics.example.com'
// Get data to draw chart
const d1 = result.results[0].visitors
const d2 = result.results[1].visitors
const d3 = result.results[2].visitors
const d4 = result.results[3].visitors
const d5 = result.results[4].visitors
const d6 = result.results[5].visitors
const d7 = result.results[6].visitors
const d8 = result.results[7].visitors
const d9 = result.results[8].visitors
const d10 = result.results[9].visitors
const d11 = result.results[10].visitors
const d12 = result.results[11].visitors
const d13 = result.results[12].visitors
const d14 = result.results[13].visitors
const d15 = result.results[14].visitors
const d16 = result.results[15].visitors
const d17 = result.results[16].visitors
const d18 = result.results[17].visitors
const d19 = result.results[18].visitors
const d20 = result.results[19].visitors
const d21 = result.results[20].visitors
const d22 = result.results[21].visitors
const d23 = result.results[22].visitors
const d24 = result.results[23].visitors
let data = [d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15, d16, d17, d18, d19, d20, d21, d22, d23, d24];
let chart = new LineChart(400, 200, data).configure((ctx, path) => {
ctx.opaque = false;
ctx.setFillColor(new Color("6574CD", .35));
ctx.addPath(path);
ctx.fillPath(path);
}).getImage();
widget.addSpacer()
// Set chart as background
widget.backgroundImage = chart
// Display active users
if (liveVisitors == '0'){
t2 = widget.addText("● 0 online" )
t2.font = Font.boldSystemFont(13)
t2.textColor = Color.gray()
}
else {
t2 = widget.addText(copy1)
t2.font = Font.boldSystemFont(13)
t2.textColor = onlineColor
}
// Set DAU, top sources, and top country
if (result2.results.visitors.value == '0'){
tr = widget.addText("no data yet." )
tr.font = Font.boldSystemFont(13)
tr.textColor = Color.gray()
}
else {
const visitors2 = (result2.results.visitors.value)
const views = (result2.results.pageviews.value)
const sources = (result3.results[0].source)
const geo = (result4.results[0].country)
tr = widget.addText(visitors2 + " users")
tr.font = Font.boldSystemFont(13)
tr.textColor = txtColor
pv = widget.addText(views + " pageviews")
pv.font = Font.boldSystemFont(13);
pv.textColor = txtColor
tc = widget.addText(sources + " & " + geo)
tc.font = Font.boldSystemFont(13);
tc.textColor = txtColor
}
// Wrap up and set widget size
Script.setWidget(widget)
if (!config.runsInWidget) {
await widget.presentSmall()
}
Script.complete()
Note:
- example.com = your primary domain, and analytics.example.com = the subdomain where you’ve installed Plausible. You’ve to replace both accordingly.
- The widget size is 1×1. That’s a small, square size widget.
- It is only tested on self-hosted Plausible instances.
How to create the iOS widget:
Step 1: Generate an API key from https://analytics.example.com/settings.
Step 2: Install Scriptable on your iPhone/iPad.
Step 3: Get plausible.js.
Step 4: Edit the script.
- line 6, add your baseURL, eg: https://analytics.example.com/api/v1/stats/.
- line 9, add your API key.
- line 12, add your siteID, which is example.com.
- line 114, replace “Example Daily” with your brand or domain name.
- line 120, add a URL to visit after tapping the widget.
Step 5: Save the script and set your widget.