iOS/iPadOS Scriptable Widget

Begonnen von Che007, 28 März 2026, 10:12:00

Vorheriges Thema - Nächstes Thema

Che007

Hallo zusammen,

habe mir heute ein Script zusammengebastelt, welches mir "wichtigsten" Daten auf den ersten Blick zur Verfügung stellt.
Code kann einfach in die kostenlose App Scriptable eingefügt werden. Devices und Readings müssen oben natürlich angepasst werden.
Auf dem Homescreen oder unter Widget kann das Skript dann eingefügt werden:

Das Skript und Bild findet ihr anbei.

// ============================================================
//  FHEM Widget fuer Scriptable - Medium (4x2)
// ============================================================
//
//  KONFIGURATION - nur diese Zeilen anpassen:
//  Einfach die Werte nach dem = aendern, keine Anfuehrungszeichen noetig!
// ————————————————————

var FHEM_HOST       = `pi5`
var FHEM_PORT       = `8086`

var S1_LABEL        = 'Wärmepumpe'
var S1_DEVICE       = 'MQTT2_ebusd_23.3_1'
var S1_READING      = 'CurrentConsumedPower'
var S1_UNIT         = 'W'

var S2_LABEL        = 'Warmwasser'
var S2_DEVICE       = 'MQTT2_ebusd_23.3_1'
var S2_READING      = 'HwcTemp'
var S2_UNIT         = '°C'

var S3_LABEL        = 'KompressorStarts'
var S3_DEVICE       = 'di_counter_new'
var S3_READING      = 'MQTT2_ebusd_23.3_1.CompressorStarts.day'
var S3_UNIT         = ''

var P1_LABEL        = 'Strom aktuell'
var P1_DEVICE       = 'Stromzaehler'
var P1_READING      = 'MT175_Power_curr'
var P1_UNIT         = 'W'

var P2_LABEL        = 'Solar aktuell'
var P2_DEVICE       = 'kaco'
var P2_READING      = 'sac'
var P2_UNIT         = 'W'

var P3_LABEL        = 'Bezug heute'
var P3_DEVICE       = 'di_counter_new'
var P3_READING      = 'Stromzaehler.MT175_Total_in.day'
var P3_UNIT         = 'kWh'
// ============================================================
//  Ab hier nichts mehr aendern
// ============================================================

var FHEM_BASE = `http://` + FHEM_HOST + `:` + FHEM_PORT

var sensors = [
{ label: S1_LABEL, device: S1_DEVICE, reading: S1_READING, unit: S1_UNIT },
{ label: S2_LABEL, device: S2_DEVICE, reading: S2_READING, unit: S2_UNIT },
{ label: S3_LABEL, device: S3_DEVICE, reading: S3_READING, unit: S3_UNIT },
]

var power = [
{ label: P1_LABEL, device: P1_DEVICE, reading: P1_READING, unit: P1_UNIT },
{ label: P2_LABEL, device: P2_DEVICE, reading: P2_READING, unit: P2_UNIT },
{label: P3_LABEL, device: P3_DEVICE, reading: P3_READING, unit: P3_UNIT },
]

async function getCsrfToken() {
var req = new Request(FHEM_BASE + `/fhem&XHR=1`)
await req.load()
var token = req.response.headers[`X-FHEM-csrfToken`]
return token || ``
}

async function fetchReading(token, device, reading) {
var url = FHEM_BASE + `/fhem?XHR=1&fwcsrf=` + token + `&cmd=jsonlist2%20` + device
try {
var req = new Request(url)
req.timeoutInterval = 5
var json = await req.loadJSON()
var results = json && json.Results
if (!results || results.length === 0) return null
return results
.Readings && results.Readings[reading]
? results.Readings[reading].Value
: null
} catch (e) {
return null
}
}
function tempColor(val) {
var t = parseFloat(val)
if (isNaN(t))  return Color.gray()
if (t <= 0)    return new Color(`#64b5f6`)
if (t <= 15)   return new Color(`#81d4fa`)
if (t <= 22)   return new Color(`#a5d6a7`)
if (t <= 27)   return new Color(`#ffcc80`)
return new Color(`#ef9a9a`)
}
async function buildWidget() {
var token = await getCsrfToken()
var widget = new ListWidget()
widget.backgroundColor = new Color(`#1c1c1e`)
widget.setPadding(12, 14, 12, 14)
// Kopfzeile
var header = widget.addStack()
header.layoutHorizontally()
header.centerAlignContent()
var titleTxt = header.addText(`FHEM`)
titleTxt.font = Font.boldSystemFont(13)
titleTxt.textColor = Color.white()
header.addSpacer()
var now = new Date()
var timeStr = now.toLocaleTimeString(`de-DE`, { hour: `2-digit`, minute: `2-digit` })
var timeTxt = header.addText(timeStr)
timeTxt.font = Font.systemFont(11)
timeTxt.textColor = new Color(`#8e8e93`)
widget.addSpacer(8)
var body = widget.addStack()
body.layoutHorizontally()
// Linke Spalte: Temperaturen
var leftCol = body.addStack()
leftCol.layoutVertically()
leftCol.size = new Size(160, 0)
var tHead = leftCol.addText(`Wärmepumpe`)
tHead.font = Font.boldSystemFont(9)
tHead.textColor = new Color(`#636366`)
leftCol.addSpacer(5)
for (var i = 0; i < sensors.length; i++) {
var s = sensors[i]
var val = await fetchReading(token, s.device, s.reading)
var row = leftCol.addStack()
row.layoutHorizontally()
row.centerAlignContent()
var dot = row.addText(`> `)
dot.font = Font.boldSystemFont(9)
dot.textColor = val !== null ? tempColor(val) : Color.gray()
var lbl = row.addText(s.label)
lbl.font = Font.systemFont(12)
lbl.textColor = new Color(`#ebebf5`)
lbl.lineLimit = 1
row.addSpacer()
var valStr = val !== null ? (parseFloat(val).toFixed(1) + ' ' + s.unit) : `-`
var valTxt = row.addText(valStr)
valTxt.font = Font.boldSystemFont(12)
valTxt.textColor = val !== null ? tempColor(val) : Color.gray()
leftCol.addSpacer(4)
}
// Trennlinie
body.addSpacer(10)
var div = body.addStack()
div.layoutVertically()
div.size = new Size(1, 0)
div.backgroundColor = new Color(`#3a3a3c`)
body.addSpacer(10)
// Rechte Spalte: Energie
var rightCol = body.addStack()
rightCol.layoutVertically()
var pHead = rightCol.addText(`ENERGIE`)
pHead.font = Font.boldSystemFont(9)
pHead.textColor = new Color(`#636366`)
rightCol.addSpacer(5)
for (var j = 0; j < power.length; j++) {
var p = power[j]
var pVal = await fetchReading(token, p.device, p.reading)
var pRow = rightCol.addStack()
pRow.layoutHorizontally()
pRow.centerAlignContent()
var pLbl = pRow.addText(p.label)
pLbl.font = Font.systemFont(12)
pLbl.textColor = new Color(`#ebebf5`)
pLbl.lineLimit = 1
pRow.addSpacer()
var pValStr = pVal !== null ? (parseFloat(pVal).toFixed(0) + ` ` + p.unit) : `-`
var pValTxt = pRow.addText(pValStr)
pValTxt.font = Font.boldSystemFont(12)
pValTxt.textColor = pVal !== null ? new Color(`#ffd60a`) : Color.gray()
rightCol.addSpacer(4)
}
widget.addSpacer()
var footer = widget.addText(`Aktualisiert ` + timeStr + ` Uhr`)
footer.font = Font.systemFont(9)
footer.textColor = new Color(`#48484a`)
footer.centerAlignText()
return widget
}
var widget = await buildWidget()
if (config.runsInWidget) {
Script.setWidget(widget)
} else {
widget.presentMedium()
}
Script.complete()