Matomo Heartbeat -
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');
log(message, data = null) if (this.options.debug) '');
sendHeartbeat(type) const now = Date.now(); const timeSinceLastHeartbeat = (now - this.lastHeartbeatTime) / 1000; // Don't send heartbeat if too short if (timeSinceLastHeartbeat < this.options.minVisitLength && type === 'ongoing') return; const heartbeatData = action_name: 'Heartbeat', e_c: 'Engagement', e_a: type, e_n: 'User Activity', e_v: Math.floor(timeSinceLastHeartbeat), _cvar: JSON.stringify( heartbeat_interval: this.options.heartbeatInterval, time_on_page: Math.floor((now - this.visitStartTime) / 1000), total_engaged_time: this.totalEngagedTime + Math.floor((now - this.lastHeartbeatTime) / 1000) ) ; // Send to Matomo if (window._paq) window._paq.push(['trackEvent', heartbeatData.e_c, heartbeatData.e_a, heartbeatData.e_n, heartbeatData.e_v]); // Optional: track custom variable window._paq.push(['setCustomVariable', 1, 'HeartbeatType', type, 'page']); this.log(`Heartbeat sent: $type`, heartbeatData); this.lastHeartbeatTime = now; matomo heartbeat
header('Content-Type: application/json'); echo json_encode($result); <!DOCTYPE html> <html> <head> <!-- Matomo Tracking Code --> <script> var _paq = window._paq = window._paq || []; _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() var u="https://your-matomo-domain.com/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); )(); </script>
public function processHeartbeat($data) $heartbeatData = [ 'idsite' => $this->siteId, 'rec' => 1, 'action_name' => $data['action_name'] ?? 'Heartbeat', 'url' => $data['url'] ?? ($_SERVER['HTTP_REFERER'] ?? ''), 'rand' => mt_rand(), 'e_c' => $data['e_c'] ?? 'Engagement', 'e_a' => $data['e_a'] ?? 'heartbeat', 'e_n' => $data['e_n'] ?? 'User Activity', 'e_v' => $data['e_v'] ?? 0, 'cdt' => date('Y-m-d H:i:s') ]; // Add custom variables if (!empty($data['_cvar'])) $heartbeatData['_cvar'] = $data['_cvar']; // Send to Matomo return $this->sendToMatomo($heartbeatData); stopHeartbeat() if (this
private function sendToMatomo($data) $url = $this->matomoUrl . '/matomo.php?' . http_build_query($data); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 2); // Quick timeout $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode === 200;
public function getEngagementMetrics($dateFrom, $dateTo) $stmt = $this->db->prepare(" SELECT COUNT(DISTINCT session_id) as total_sessions, SUM(total_engaged_time) as total_engagement_seconds, AVG(total_engaged_time) as avg_engagement_seconds, AVG(heartbeat_count) as avg_heartbeats_per_session, COUNT(CASE WHEN total_engaged_time > 60 THEN 1 END) as long_sessions, COUNT(CASE WHEN total_engaged_time < 10 THEN 1 END) as bounce_sessions FROM matomo_heartbeat_sessions WHERE start_time BETWEEN ? AND ? AND is_active = 0 "); $stmt->execute([$dateFrom, $dateTo]); $metrics = $stmt->fetch(PDO::FETCH_ASSOC); if ($metrics['total_sessions'] > 0) $metrics['bounce_rate'] = ($metrics['bounce_sessions'] / $metrics['total_sessions']) * 100; $metrics['engagement_rate'] = ($metrics['long_sessions'] / $metrics['total_sessions']) * 100; return $metrics; ''), 'rand' => mt_rand(), 'e_c' => $data['e_c']
public function getEngagementReport($dateRange = 'last30') $apiUrl = $this->matomoUrl . '/index.php'; $params = [ 'module' => 'API', 'method' => 'CustomEvents.getCustomEvent', 'idSite' => $this->siteId, 'period' => 'range', 'date' => $dateRange, 'format' => 'json', 'filter_limit' => -1 ]; if ($this->tokenAuth) $params['token_auth'] = $this->tokenAuth; $url = $apiUrl . '?' . http_build_query($params); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true);