$url, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => $SSLExpired ? 0 : 1, CURLOPT_HTTPHEADER => $headers ]); if ($method === 'POST') { curl_setopt($ch, CURLOPT_POST, true); if ($body) curl_setopt($ch, CURLOPT_POSTFIELDS, is_string($body) ? $body : json_encode($body)); } $res = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); $err = curl_error($ch); curl_close($ch); if ($debug) echo ">>> $method $url\n HTTP $http: $res\n ERR: $err\n\n"; return json_decode($res, true); } // 1. Login $r = api(BASE . '/web/sems/sems-user/api/v1/auth/cross-login', 'POST', [ 'account' => $sems_email, 'pwd' => base64_encode(md5($sems_password)), 'isChinese' => false, 'agreement' => 1, 'isLocal' => true ]); if (!$r || $r['code'] !== '00000') exit("ERROR:1 Login failed\n"); $d = $r['data']; $token = json_encode([ 'uid' => $d['uid'], 'timestamp' => $d['timestamp'], 'token' => $d['token'], 'client' => $d['client'], 'version' => $d['version'], 'language' => $d['language'], 'region' => $d['region'] ]); // 2. Stations page -> stationId, pSystem, productionToday $r = api(BASE . '/web/sems/sems-plant/api/app/v2/stations/page', 'POST', '{"current":1,"size":2}', $token); if (!$r || $r['code'] !== '00000') exit("ERROR:2 Stations failed: " . ($r['description'] ?? '') . "\n"); $s = $r['data']['dataList'][0]; $sid = $s['id']; $psystem = $s['pSystem'] ?? 0; $prod_today = $s['productionToday'] ?? 0; // 3. Device list -> inverter SN $r = api(BASE . "/web/sems/sems-plant/api/stations/device/all-status?stationId=$sid", 'GET', null, $token); if (!$r || $r['code'] !== '00000') exit("ERROR:3 Devices failed: " . ($r['description'] ?? '') . "\n"); $sn = $r['data']['deviceDetailList'][0]['statusDetailList'][0]['snList'][0]; // 4. Map to PVOutput $power_w = intval(floatval($psystem) * 1000); $energy_wh = round(floatval($prod_today) * 1000); // 5. Output (cron-friendly: one line with key values) if ($debug) echo "SN=$sn Energy={$energy_wh}Wh Power={$power_w}W\n"; if ($test) { echo "TEST: would submit d=" . date('Ymd') . " t=" . date('H:i') . " v1=$energy_wh v2=$power_w\n"; exit(0); } // 6. Submit to PVOutput $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => 'https://pvoutput.org/service/r2/addstatus.jsp', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_SSL_VERIFYPEER => $SSLExpired ? 0 : 1, CURLOPT_POSTFIELDS => http_build_query([ 'd' => date('Ymd'), 't' => date('H:i'), 'v1' => $energy_wh, 'v2' => $power_w ]), CURLOPT_HTTPHEADER => [ 'X-Pvoutput-Apikey: ' . $pv_key, 'X-Pvoutput-SystemId: ' . $pv_sid ] ]); $pv = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code === 200) { echo "OK d=" . date('Ymd') . " t=" . date('H:i') . " v1=$energy_wh v2=$power_w\n"; exit(0); } echo "ERROR:4 PVOutput HTTP $code $pv\n"; exit(1);