Biz YANGI het.uz akkauntidan ma’lumotlarni sizning aqlli uyingizga o’tkazmoqdamiz.

Ilgari men elektr energiyasi iste’molchisining shaxsiy hisobidan balansni qanday tahlil qilish haqida maqola yozgan edim. Parserning oldingi versiyasidan (va hisobning o’zidan) foydalangan har bir kishi, ehtimol, tahlil qilish beqaror va nihoyatda sekin ekanligini payqagan bo’lishi mumkin. Va endi, kutilmaganda, HET nihoyat qurigan sh… ahem… baribir o’zgartirmoqda! Ularda yangi hisob bor. Agar ushbu yangi hisobni ishlab chiquvchilar ushbu maqolani o’qiyotgan bo’lsa, sizga salom va hurmatim! Va boshqalar uchun men xushxabarni ulashishga shoshilaman: ushbu yangi hisob qanday yaratilganligini hisobga olsak, endi hech narsani tahlil qilishning hojati yo’q! Chunki endi siz ma’lumotlarni to’g’ridan-to’g’ri API dan osongina olishingiz mumkin, bu bilan yangi hisobning o’zi ishlaydi. F12 tugmasini bosish orqali siz JS ilovasi nimani, qanday va qayerdan yuklayotganini kuzatishingiz mumkin. Aytgancha, ma’lumotlar juda tez yangilanadi. Masalan, hisobingizga amalga oshirilgan to’lov deyarli bir zumda hisobingizda paydo bo’ladi. Lekin bu ofis qanchalik chiroyli va qulay bo’lsa ham, menga biroz ko’proq kerak. Bu safar men ma’lumotlar darhol aqlli uyning qon aylanish tizimiga, ya’ni to’g’ridan-to’g’ri MQTTga kirishiga ishonch hosil qilishga qaror qildim.

Birinchidan, bizga PHP uchun MQTT mijoz kutubxonasi kerak bo’ladi. Men bu yerda topgan birinchisini tanladim: https://github.com/php-mqtt/client va u juda yaxshi ishladi.

Biz skriptimiz joylashgan papkaga kiramiz va kutubxonani o’rnatamiz

 composer require php-mqtt/client 

Keyin, hisob ma’lumotlari va ba’zi sozlamalarni o’z ichiga olgan ishga tushirish faylini yarating. Siz uni xohlagancha nomlashingiz mumkin.

 <pre class="wp-block-syntaxhighlighter-code"><?php
	define('HET_LOGIN',"номер счёта");
	define('HET_PASS',"Пароль");
	
	$server   = 'хост MQTT брокера';
	$port     = 1883;
	$clientId = 'het-informer';
	$mqttLogin = "логин от mqtt";
	$mqttPass = "пароль от mqtt";
	// $balancefile = "dom.txt"; 
        // Раскомментируй строку выше, если хочешь кешировать данные о балансе, чтобы можно было забирать их как статику. В моём случае это нужно для работы навыка Алисы.
	require_once ('het.php');</pre> 

Keyingi fayl het.php deb ataladi

 <pre class="wp-block-syntaxhighlighter-code"><?php
	if(!defined('HET_LOGIN')){
		die();
	}

	require_once 'vendor/autoload.php'; //Подгружаем библиотеку mqtt
	
	$ch = curl_init();
	curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); 
	curl_setopt($ch, CURLOPT_TIMEOUT, 5);
	curl_setopt($ch, CURLOPT_URL, 'https://cabinet-api.het.uz/household-consumer/v1/mobile-cabinet/user-login'); // Тут у нас адрес для авторизации. Мы должны отправить туда json массив с учётными данными.
	curl_setopt($ch, CURLOPT_USERAGENT,'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36'); // Прикидываемся браузером, чтобы не спровоцировать какой-нибудь фильтр. Раньше у них такой стоял...
	curl_setopt($ch, CURLOPT_POST, true); // отправлять будем методом POST
	curl_setopt($ch, CURLOPT_POSTFIELDS, '{"login":"'.HET_LOGIN.'", "password":"'.HET_PASS.'"}'); // Собственно, массив с учётными данными
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($ch, CURLOPT_HTTP09_ALLOWED, true);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
	$headers = array(
	   "Connection: keep-alive",
	   "Keep-Alive: timeout=5, max=100",
	   "Content-Type: application/json", // вот тут обязательно указываем тип данных, иначе API пошлёт нас нафиг.
	);
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
	$authfile = '/tmp/el'.HET_LOGIN.'.json'; // тут мы создаём файл, куда будем прятать токен авторизации. Попытки постоянных повторных автоиризаций во время тестирования завели меня во временный бан. Больше так не делаем. :)

	if(file_exists($authfile)){
		if($cache = file_get_contents($authfile)){
			if(stristr($cache,'Successfully')){
				$answer = $cache;
				$cache = json_decode($cache, true);
				if($cache["timestamp"]+$cache["data"]["expiresIn"] > time()){ // Проверяем актуальность токена, взятого из файла. При любой непонятной ситуации включаем переавторизацию. 
					$refresh = false;
				} else {
					$refresh = true;
				}
			} else {
				$refresh = true;
			}
		} else {
			$refresh = true;
		}
	} else {
		
		$refresh = true;
	}

	if($refresh == true){
		echo "REFRESH\r\n";
		$answer = curl_exec($ch);
		if (curl_error($ch)) {
			echo curl_error($ch);
		}
		if(stristr($answer,'Successfully')){
			file_put_contents('/tmp/el'.HET_LOGIN.'.json',$answer);
		}
	}
	if(stristr($answer,'Successfully')){
		$answer = json_decode($answer, true);
	} else {
                unlink($authfile); // Если что-то пошло не так, грохаем кеш авторизации и завершаем работу скрипта, ибо дальше в ней нет смысла.
		die('unsuccess');
	}
			
	$token = $answer["data"]["accessToken"]; // Ну так, для наглядности. Конечно, модно было не плодить переменные, но чё нам, жалко памяти? ;)

	curl_setopt($ch, CURLOPT_URL, 'https://cabinet-api.het.uz/household-consumer/v1/mobile-cabinet/consumer-state'); // Запрашиваем данные со нужного раздела API
	curl_setopt($ch, CURLOPT_POST, false);
	$headers = array(
	   "Connection: keep-alive",
	   "Keep-Alive: timeout=5, max=100",
	   "Authorization: Bearer ".$token, //Добавляем токен авторизации в заголовок нового запроса.
	   "Coato-Code: ".$answer["data"]["coatoCode"], //Тут требуется добавить номер РЭС. Берётся так-же из ответа на авторизацию.
	);
	curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
	$answer = curl_exec($ch); // Пуляем запрос

	if (curl_error($ch)) {
		echo curl_error($ch);
	}
	curl_close($ch);
	print_r($answer); // Смотрим, что прилетело. В принципе, это можно закомментить
	if(stristr($answer,'Successfully')){
		
		$answer = json_decode($answer,true); // Превращаем JSON в обычный массив
		$mqtt = new \PhpMqtt\Client\MqttClient($server, $port, $clientId); // создаём экземпляр для MQTT
		
		if(isset($balancefile))
			file_put_contents($balancefile,$answer["data"]["balance"] / 100); // Складываем данные о балансе в статический файл, если включено в настройках.
		
		$connectionSettings = (new \PhpMqtt\Client\ConnectionSettings)
			->setConnectTimeout(3)
			->setUsername($mqttLogin)
			->setPassword($mqttPass); // тут у нас добавляются данные для авторизации в mqtt. Если у вас там всё открыто (чего я категорически не советую), можно это выпилить и сделать $mqtt->connect() без аргументов.
		
		$mqtt->connect($connectionSettings, true);
		if($answer["data"]["balance"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/balance', $answer["data"]["balance"] / 100, 1);
		if($answer["data"]["lastCrawlReading"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/lastCrawlReading', $answer["data"]["lastCrawlReading"] / 1000, 0);
		if($answer["data"]["lastCrawlDate"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/lastCrawlDate', $answer["data"]["lastCrawlDate"], 0);
		if($answer["data"]["lastPayment"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/lastPayment', $answer["data"]["lastPayment"] / 100, 0);
		if($answer["data"]["lastPaymentDate"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/lastPaymentDate', $answer["data"]["lastPaymentDate"], 0);
		if($answer["data"]["currentMonthCalcKwh"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/currentMonthCalcKwh', $answer["data"]["currentMonthCalcKwh"] / 1000, 0);	
		if($answer["data"]["currentMonthCalcSum"]!==null)
			$mqtt->publish('het/'.HET_LOGIN.'/currentMonthCalcSum', $answer["data"]["currentMonthCalcSum"] / 100, 0);
		$mqtt->publish('het/'.HET_LOGIN.'/ecoCurrentMonthCalcKwh', $answer["data"]["ecoCurrentMonthCalcKwh"]/1000, 0);

// Значения, которые по идее, должны быть дробными приходят целыми числами, по этому приходится делать соответствующие деления. Ну, либо можно оставить это на откуп узлам умного дома. Но мне было проще вот так. В некоторых случаях API отдаёт значение null, что в результате роняет библиотеку mqtt, по этому делаем проверку и не отправляем ничего со значением null. 
		$mqtt->disconnect(); // Закрываем соединение
	} else {
		unlink($authfile); // Если в ответ на запрос данных пришло не то, что нужно, грохаем кеш авторизации, потому, что причина скорее всего в ней.
	}
?></pre> 

Agar biron sababga ko’ra avtorizatsiya amalga oshmasa, skript keyingi safar ishga tushirilganda uni yana so’raydi. Men rekursiv iteratsiyalardan foydalanmadim, chunki bu API tomonida taqiqqa olib kelishi mumkin. Skriptni cron orqali ishga tushirish, veb-serverga yuklash va aqlli uy avtomatlashtirish orqali chaqirish mumkin.

P.S.
Yangi boshqaruv paneli ishlab chiquvchilariga katta rahmat! Besh yil oldin men ma’lumotlarni olish uchun het.uz dan API olishga harakat qilgan edim, ammo barcha elektron pochta xabarlarim e’tiborga olinmadi.

Nega bu ma’lumotlarni to’plash kerak? Masalan, agar uyim yoki yozgi uyimdagi hisoblagich qoldig’i 30 000 so’mdan kam bo’lsa, oilaviy Telegram guruhida avtomatik bildirishnoma o’rnatdim. Hatto hisob-fakturalarni avtomatik ravishda yaratmoqchi edim, ammo hozircha hech bir to’lov tizimi menga bu g’oyani amalga oshirishda yordam berishga tayyor emas. Umid qilamanki, bu qachondir amalga oshadi. :)