diff --git a/src/meshtastic-card.js b/src/meshtastic-card.js index b7ec35f..a09a5fa 100644 --- a/src/meshtastic-card.js +++ b/src/meshtastic-card.js @@ -9,9 +9,15 @@ class MeshtasticCard extends LitElement { return { hass: {}, config: {}, + _nodesExpanded: { type: Boolean }, }; } + constructor() { + super(); + this._nodesExpanded = false; + } + static getConfigForm() { return { schema: [ @@ -62,6 +68,35 @@ class MeshtasticCard extends LitElement { return `${d > 0 ? d + 'd ' : ''}${h > 0 ? h + 'h ' : ''}${m}m`; } + _formatRelativeTime(utcString) { + const match = utcString.match(/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2}) UTC/); + if (!match) return utcString; + const then = Date.UTC(match[1], match[2] - 1, match[3], match[4], match[5], match[6]); + const diffSec = Math.floor((Date.now() - then) / 1000); + if (diffSec < 60) return "just now"; + const m = Math.floor(diffSec / 60); + if (m < 60) return `${m} min ago`; + const h = Math.floor(m / 60); + const rm = m % 60; + if (h < 24) return rm > 0 ? `${h}h ${rm}min ago` : `${h}h ago`; + const d = Math.floor(h / 24); + return `${d}d ${h % 24}h ago`; + } + + _parseOnlineNodes(stateObj) { + const list = stateObj?.attributes?.online_nodes; + if (!Array.isArray(list)) return []; + return list.map(entry => { + const match = entry.match(/^(.+?) \(last heard: (.+)\)$/); + if (!match) return { name: entry, ago: "" }; + return { name: match[1], ago: this._formatRelativeTime(match[2]) }; + }); + } + + _toggleNodes() { + this._nodesExpanded = !this._nodesExpanded; + } + _renderBar(label, stateObj, icon, color, showPower = false, isPowered = false) { const val = parseFloat(stateObj?.state) || 0; return html` @@ -117,9 +152,27 @@ class MeshtasticCard extends LitElement {