Version 1

This commit is contained in:
2026-04-13 19:52:44 +02:00
parent 8663446ada
commit 071d6d9253
6 changed files with 1576 additions and 2 deletions
+14
View File
@@ -0,0 +1,14 @@
TECHEM_EMAIL=user@email.de
TECHEM_PASSWORD=your-secret-password
# Die ID bekommst du in der URL, wenn du die Detailseite deiner Einheit auf der Techem-Webseite aufrufst.
TECHEM_UNIT_ID=PRUN:HZ3:DEU01:xxxxxxxxxxxxxxxxxxxxx
# Export period (YYYY-MM)
START_DATE=2024-01
END_DATE=2024-12
# Timeouts in Millisekunden (z.B. 60000 = 60 Sekunden)
TIMEOUT_PAGE=60000
TIMEOUT_SELECTOR=15000
# Falls Probleme auftreten, setze auf true = Browser sichtbar, false = headless
DEBUG_MODE=false
+33 -2
View File
@@ -1,2 +1,33 @@
# techem-exporter
A script that automates the export of heating data from Techem meters - mieter.techem.de
# Techem Smart Exporter
Ein automatisiertes Tool zum Export von Verbrauchsdaten (Heizung & Warmwasser) aus dem Techem Mieterportal in eine strukturierte Excel-Tabelle. Das Skript nutzt Playwright zur Browser-Automatisierung und navigiert effizient durch die monatlichen Verbrauchsübersichten.
## Features
- **Automatischer Login**: Handhabt den Azure AD B2C Login-Prozess.
- **Cookie-Banner Bypass**: Erkennt und schließt den Cookiebot-Banner automatisch.
- **Intelligente Extraktion**: Nutzt Regex-Parsing, um Heizungs- und Wasserwerte direkt aus dem DOM zu lesen.
- **Smart Formatting**: Erstellt eine Excel-Datei im Horizontal-Layout (Monat | Warmwasser | Heizung).
- **Admin-Ready**: Vollständig über `.env` konfigurierbar mit anpassbaren Timeouts und Debug-Modus.
## Voraussetzungen
- Node.js (v16 oder höher)
- npm (Node Package Manager)
## Nutzung
Starte den Export mit:
```bash
npm i
node index.js
```
Das Skript öffnet (im Debug-Modus) den Browser, loggt sich ein und iteriert durch alle Monate. Die fertige Datei wird als `Techem_Smart_Export_DATUM.xlsx` im Projektordner gespeichert.
## Troubleshooting
- **Hängt beim Login?**: Setze `DEBUG_MODE=true` und beobachte, ob Techem ein Captcha oder eine MFA-Abfrage verlangt.
- **Keine Daten gefunden?**: Das Skript nutzt Regex zur Suche nach "Heating" und "Water". Sollte Techem die Sprache des Portals ändern, müssen die Begriffe in der `index.js` ggf. angepasst werden.
- **Timeouts?**: Erhöhe `TIMEOUT_PAGE` in der `.env`, falls deine Internetverbindung oder das Techem-Portal langsam reagieren.
## Lizenz
MIT - Feel free to PR :P
Binary file not shown.
+152
View File
@@ -0,0 +1,152 @@
require('dotenv').config();
const { chromium } = require('playwright');
const ExcelJS = require('exceljs');
const dayjs = require('dayjs');
async function runExport() {
const isDebug = process.env.DEBUG_MODE === 'true';
const config = {
email: process.env.TECHEM_EMAIL,
password: process.env.TECHEM_PASSWORD,
unitId: process.env.TECHEM_UNIT_ID,
startDate: process.env.START_DATE,
endDate: process.env.END_DATE,
pageTimeout: parseInt(process.env.TIMEOUT_PAGE, 10) || 60000,
selectorTimeout: parseInt(process.env.TIMEOUT_SELECTOR, 10) || 15000,
};
console.log(`🚀 Techem Export (Horizontal-Layout) gestartet...`);
const browser = await chromium.launch({
headless: !isDebug,
slowMo: isDebug ? 250 : 0
});
const context = await browser.newContext();
const page = await context.newPage();
try {
// --- LOGIN FLOW ---
console.log('🔑 Login-Prozess...');
await page.goto('https://mieter.techem.de/', { waitUntil: 'networkidle', timeout: config.pageTimeout });
// Cookie-Banner weg
try {
const cookieBtn = await page.waitForSelector('#CybotCookiebotDialogBodyButtonDecline', { timeout: 5000 });
if (cookieBtn) await cookieBtn.click();
} catch (e) {}
// Login-Bereich öffnen & Formular ausfüllen
const loginTrigger = page.locator('text=/login|anmelden/i').first();
await loginTrigger.click();
await page.waitForSelector('input[type="email"]', { timeout: config.selectorTimeout });
await page.fill('input[type="email"]', config.email);
await page.fill('input[type="password"]', config.password);
await page.click('button[type="submit"]');
await page.waitForURL(/.*consumptions.*/, { timeout: config.pageTimeout });
console.log('✅ Login erfolgreich.');
// --- DATEN EXTRAKTION ---
const months = [];
let current = dayjs(config.startDate);
const end = dayjs(config.endDate);
while (current.isBefore(end) || current.isSame(end)) {
months.push(current.format('YYYY-MM'));
current = current.add(1, 'month');
}
const results = [];
for (const date of months) {
console.log(`📡 Lade Daten für ${date}...`);
let hWert = 0, hUnit = 'kWh';
let wWert = 0, wUnit = 'm³';
try {
// 1. Heizung abrufen
const urlHeating = `https://mieter.techem.de/en/${config.unitId}/consumptions/heating/${date}`;
// Turbo an: Wir warten nur auf das DOM, nicht auf Tracking-Skripte
await page.goto(urlHeating, { waitUntil: 'domcontentloaded', timeout: config.pageTimeout });
await page.waitForTimeout(1000); // Kurze 1-Sekunden-Atempause für React
const bodyHeating = await page.innerText('body');
const matchH = bodyHeating.match(/([\d,.]+)\s*(kWh|units)/i);
if (matchH) {
hWert = parseFloat(matchH[1].replace(',', '.'));
hUnit = matchH[2];
}
// 2. Warmwasser abrufen
const urlWater = `https://mieter.techem.de/en/${config.unitId}/consumptions/hot-water/${date}`;
await page.goto(urlWater, { waitUntil: 'domcontentloaded', timeout: config.pageTimeout });
await page.waitForTimeout(1000);
const bodyWater = await page.innerText('body');
const matchW = bodyWater.match(/([\d,.]+)\s*(m³|kWh)/i);
if (matchW) {
wWert = parseFloat(matchW[1].replace(',', '.'));
wUnit = matchW[2];
}
// Daten zusammenführen
results.push({
monat: date,
water: wWert,
waterUnit: wUnit,
heating: hWert,
heatingUnit: hUnit
});
console.log(` ✅ HZ: ${hWert} ${hUnit} | WW: ${wWert} ${wUnit}`);
} catch (err) {
console.error(`⚠️ Fehler bei ${date}: ${err.message}`);
}
}
// --- EXCEL DESIGN ---
console.log('📊 Erstelle übersichtliche Excel-Datei...');
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet('Verbrauchsübersicht');
sheet.columns = [
{ header: 'Monat', key: 'monat', width: 15 },
{ header: 'Verbrauch Warmwasser', key: 'water', width: 25 },
{ header: 'Einheit WW', key: 'waterUnit', width: 12 },
{ header: 'Verbrauch Heizung', key: 'heating', width: 25 },
{ header: 'Einheit HZ', key: 'heatingUnit', width: 12 }
];
// Styling (Blauer Header, alternierende Zeilen)
sheet.getRow(1).font = { bold: true, color: { argb: 'FFFFFFFF' } };
sheet.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF00549F' } };
sheet.getRow(1).alignment = { vertical: 'middle', horizontal: 'center' };
results.forEach((data, index) => {
const row = sheet.addRow(data);
row.alignment = { vertical: 'middle', horizontal: 'center' };
if (index % 2 !== 0) {
row.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFF2F2F2' } };
}
});
sheet.autoFilter = 'A1:E1';
sheet.views = [{ state: 'frozen', ySplit: 1 }];
const fileName = `Techem_Smart_Export_${dayjs().format('YYYY-MM-DD')}.xlsx`;
await workbook.xlsx.writeFile(fileName);
console.log(`✨ Datei wurde generiert: ${fileName}`);
} catch (err) {
console.error('❌ Kritischer Fehler:', err.message);
} finally {
await browser.close();
}
}
runExport();
+1346
View File
File diff suppressed because it is too large Load Diff
+31
View File
@@ -0,0 +1,31 @@
{
"name": "techem-exporter",
"version": "1.0.0",
"description": "A script that automates the export of heating data from Techem meters - mieter.techem.de",
"keywords": [
"automates",
"export",
"techem"
],
"homepage": "https://github.com/MrUnknownDE/techem-exporter#readme",
"bugs": {
"url": "https://github.com/MrUnknownDE/techem-exporter/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/MrUnknownDE/techem-exporter.git"
},
"license": "MIT",
"author": "MrUnknownDE",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"axios": "^1.15.0",
"dotenv": "^17.4.2",
"exceljs": "^4.4.0",
"playwright": "^1.59.1"
}
}