Matomo Heartbeat Here

startIdleMonitoring() this.idleCheckId = setInterval(() => const idleTime = (Date.now() - this.lastActivity) / 1000; if (idleTime >= this.options.idleTimeout && this.isActive) this.stopHeartbeat(); this.log(`User idle for $idleTime seconds, stopping heartbeat`); , 1000);

sendFinalHeartbeat() if (this.isActive && this.lastHeartbeatTime) const engagedTime = Math.floor((Date.now() - this.lastHeartbeatTime) / 1000); if (engagedTime >= this.options.minVisitLength) const finalData = action_name: 'Final Heartbeat', e_c: 'Engagement', e_a: 'end', e_n: 'Session End', e_v: engagedTime, total_engaged: this.totalEngagedTime + engagedTime ; if (window._paq) window._paq.push(['trackEvent', finalData.e_c, finalData.e_a, finalData.e_n, finalData.e_v]); // Use synchronous ping for final heartbeat this.sendBeacon(finalData);

}

resetIdleTimer() this.lastActivity = Date.now(); // If user becomes active again after idle if (!this.isActive && !document.hidden) this.startHeartbeat();

I'll help you develop a Matomo Heartbeat feature. This feature tracks user engagement time more accurately by sending periodic heartbeats to Matomo analytics. 1. JavaScript Heartbeat Tracker // matomo-heartbeat.js class MatomoHeartbeat { constructor(options = {}) this.options = heartbeatInterval: 15, // seconds minVisitLength: 5, // minimum seconds for a valid visit idleTimeout: 30, // seconds of inactivity to stop heartbeat debug: false, ...options ; this.intervalId = null; this.idleCheckId = null; this.lastActivity = Date.now(); this.visitStartTime = null; this.totalEngagedTime = 0; this.isActive = false; this.lastHeartbeatTime = null; this.init(); matomo heartbeat

public function recordHeartbeat($sessionId, $visitorId, $pageUrl, $engagedTime = 0) $stmt = $this->db->prepare(" SELECT id, total_engaged_time, last_heartbeat FROM matomo_heartbeat_sessions WHERE session_id = ? AND is_active = 1 "); $stmt->execute([$sessionId]); $session = $stmt->fetch(PDO::FETCH_ASSOC); $now = new DateTime(); if ($session) // Update existing session $lastHeartbeat = new DateTime($session['last_heartbeat']); $interval = $now->getTimestamp() - $lastHeartbeat->getTimestamp(); // Validate heartbeat interval if ($interval <= $this->heartbeatTimeout) $newTotalTime = $session['total_engaged_time'] + min($engagedTime, $interval); $update = $this->db->prepare(" UPDATE matomo_heartbeat_sessions SET last_heartbeat = ?, total_engaged_time = ?, heartbeat_count = heartbeat_count + 1 WHERE id = ? "); $update->execute([$now->format('Y-m-d H:i:s'), $newTotalTime, $session['id']]); return ['status' => 'updated', 'total_time' => $newTotalTime]; else // Session expired $this->endSession($session['id']); return $this->createNewSession($sessionId, $visitorId, $pageUrl); else // Create new session return $this->createNewSession($sessionId, $visitorId, $pageUrl);

stopHeartbeat() if (this.intervalId) clearInterval(this.intervalId); this.intervalId = null; // Calculate engaged time for this session if (this.lastHeartbeatTime) const engagedTime = Math.floor((Date.now() - this.lastHeartbeatTime) / 1000); if (engagedTime >= this.options.minVisitLength) this.totalEngagedTime += engagedTime; this.isActive = false; this.log('Heartbeat stopped'); startIdleMonitoring() this

init() this.bindEvents(); this.startIdleMonitoring(); this.log('Matomo Heartbeat initialized');