caiyunAdapter.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <?php
  2. namespace Heanup\Library\WeatherAdapt;
  3. use Heanup\App\Api\Config\CityToCoordinate;
  4. use Heanup\App\Api\Config\GlobalConfig;
  5. use Heanup\Library\Curl;
  6. use Heanup\Library\Redis;
  7. use Heanup\Library\Warming;
  8. class caiyunAdapter implements Weather
  9. {
  10. private $city_id;
  11. private $location;
  12. /**
  13. * @param $city_id
  14. * @return mixed
  15. */
  16. public function getWeatherData($city_id)
  17. {
  18. $this->city_id = $city_id;
  19. // $city_id = is_numeric($city_id) ? ("CN" . $city_id) : $city_id;
  20. $key = $this->getKey();
  21. $caiyun_version=GlobalConfig::getData("caiyun_version");
  22. $url = sprintf("https://api.caiyunapp.com/v%s/%s/%s/weather_2.json?alert=true&dailysteps=15",$caiyun_version, $key, $this->getCoodinate($city_id));
  23. $data = Curl::get($url);
  24. $origin = $data = json_decode($data, true);
  25. $data = $this->parseData($data);
  26. $i = 0;
  27. while ($data['Weather']['now']['cond_txt'] == "" && $i < 2) {//数据有误,重试3次
  28. $log_file = APP_DIR . "/logs/" . date("Ymd") . "_null.log";
  29. file_put_contents($log_file, sprintf("%s\t\tcity_id:%s", date("Y-m-d H:i:s"), $city_id) . "\t\tdata:" . json_encode($origin) . "\r\n", FILE_APPEND);
  30. Warming::semdMail("no_data", "caiyun", ALERT_TOTAL, $log_file);
  31. $data = Curl::get($url);
  32. $origin = $data = json_decode($data, true);
  33. $data = $this->parseData($data);
  34. $i++;
  35. }
  36. return $data;
  37. }
  38. private function getKey()
  39. {
  40. $keys = GlobalConfig::getData("caiyun_key");
  41. $key = date("Ymd_") . $keys;
  42. Redis::instance()->incr($key);
  43. return $keys;
  44. }
  45. /**
  46. *
  47. * @param $data
  48. * @param null $data2
  49. * @return array
  50. */
  51. public function parseData($data, $data2 = null)
  52. {
  53. $originData = $data;
  54. $return = [];
  55. $weather = [];
  56. $weather['basic'] = [
  57. 'location' => $this->location,
  58. 'city_code' => $this->city_id,
  59. 'update' => date("Y-m-d H:i", $data['server_time'])
  60. ];
  61. $weather['status'] = $originData['status'] ?: "ok";
  62. $result = $originData['result'];
  63. // $weather['key'] = $k;
  64. $weather['air_now_city'] = [
  65. "aqi" => (string)$result['realtime']['air_quality']['aqi']['chn'] ?? "", // ->a3
  66. "qlty" => (string)$result['realtime']['air_quality']['description']['chn'] ?? "", // ->a4
  67. "pm25" => (string)$result['realtime']['air_quality']['pm25'] ?? "", // ->a5
  68. "pm10" => (string)$result['realtime']['air_quality']['pm10'] ?? "", // ->a6
  69. "no2" => (string)$result['realtime']['air_quality']['no2'] ?? "", // ->a7
  70. "so2" => (string)$result['realtime']['air_quality']['so2'] ?? ""
  71. ];
  72. $weather['alarm'] = [
  73. "level" => "", // ->a9
  74. "link" => $this->getAlert($result['alert']['content'][0]['code']), // ->b1
  75. "type" => "", // ->b2
  76. 'alertId' => (string)$result['alert']['content'][0]['alertId'] ?? "",
  77. "title" => (string)$result['alert']['content'][0]['title'] ?? "",// ->z2
  78. "description" => (string)($result['alert']['content'][0]['description'] ? $result['alert']['content'][0]['description'] . sprintf("(来源:%s)", $result['alert']['content'][0]['source']) : ""), // ->z3
  79. "pubtimestamp" => (string)($result['alert']['content'] ? date("Y-m-d H:i", $result['alert']['content'][0]['pubtimestamp']) : ""), // ->z4
  80. "location" => (string)$result['alert']['content'][0]['location'] ?? "", // ->z5
  81. "status" => (string)$result['alert']['content'][0]['status'] ?? "" // ->z6
  82. ];
  83. $weather['now'] = [
  84. "cond_txt" => $this->getSkycon($result['realtime']['skycon']), // ->b3
  85. "fl" => (string)(intval($result['realtime']['apparent_temperature']) ?: ""),// ->b4
  86. "hum" => (string)intval($result['realtime']['humidity'] * 100), // ->b5
  87. "pres" => (string)intval($result['realtime']['pressure'] / 100), // ->b6
  88. "tmp" => (string)(intval($result['realtime']['temperature']) ?: ""), // ->b7
  89. "vis" => (string)$result['realtime']['visibility'] ?: "", // ->b8
  90. "wind_dir" => (string)$this->getWindDirect($result['realtime']['wind']['direction']) ?: "", // ->b9
  91. "wind_sc" => (string)$this->getWindSpeed($result['realtime']['wind']['speed'])['level'] ?: "", // ->c1
  92. "sr" => (string)$result['daily']['astro'][0]['sunrise']['time'], // ->c2
  93. "ss" => (string)$result['daily']['astro'][0]['sunset']['time'], // ->c3
  94. 'forecast_keypoint' => (string)$result['forecast_keypoint'] ?? "",
  95. 'description' => (string)$result['hourly']['description'] ?? "",
  96. ];
  97. if (!empty($result['daily'])) {
  98. foreach ($result['daily']['skycon_08h_20h'] as $key => $value) {
  99. $temp = [
  100. "cond_txt_d" => $this->getSkycon($value['value']),
  101. "cond_txt_n" => $this->getSkycon($result['daily']['skycon_20h_32h'][$key]['value']),
  102. "date" => date("Y-m-d", strtotime($value['date'])),
  103. "tmp_max" => (string)intval($result['daily']['temperature'][$key]['max']),
  104. "tmp_min" => (string)intval($result['daily']['temperature'][$key]['min']),
  105. "wind_dir" => (string)$this->getWindDirect($result['daily']['wind'][$key]['avg']['direction']),
  106. "wind_sc" => (string)$this->getWindSpeed($result['daily']['wind'][$key]['avg']['speed'])['level'],
  107. ];
  108. $weather['daily_forecast'][] = $temp;
  109. }
  110. }
  111. if (!empty($result['hourly'])) {
  112. $i = 0;
  113. foreach ($result['hourly']['temperature'] as $key => $value) {
  114. if ($i >= 24) {
  115. break;
  116. }
  117. $i++;
  118. $temp = [
  119. 'cond_txt' => $this->getSkycon($result['hourly']['skycon'][$key]['value']),
  120. 'time' => date("Y-m-d H:i", strtotime($value['datetime'])),
  121. 'tmp' => (string)intval($value['value']),
  122. ];
  123. $weather['hourly'][] = $temp;
  124. }
  125. }
  126. $weather['lifestyle'] = [
  127. [
  128. 'type' => 'flu',
  129. 'brf' => $result['daily']['life_index']['coldRisk'][0]['desc'],
  130. 'txt' => ''
  131. ], [
  132. 'type' => 'cw',
  133. 'brf' => $result['daily']['life_index']['carWashing'][0]['desc'],
  134. 'txt' => ''
  135. ], [
  136. 'type' => 'uv',
  137. 'brf' => $result['daily']['life_index']['ultraviolet'][0]['desc'],
  138. 'txt' => ''
  139. ], [
  140. 'type' => 'comf',
  141. 'brf' => $result['daily']['life_index']['comfort'][0]['desc'],
  142. 'txt' => ''
  143. ], [
  144. 'type' => 'drsg',
  145. 'brf' => $result['daily']['life_index']['dressing'][0]['desc'],
  146. 'txt' => ''
  147. ],
  148. ];
  149. $return['Weather'] = $weather;
  150. $return['source'] = "caiyun";
  151. return $return;
  152. }
  153. /**
  154. * @param $code
  155. * @return array|mixed|string
  156. */
  157. private function getCoodinate($code)
  158. {
  159. $key = "coodinateData";
  160. $coodinate = Redis::instance()->hGet($key, $code);
  161. // unset($coodinate);
  162. if (!$coodinate['lo'] || !$coodinate['la']) {
  163. $coodinate = CityToCoordinate::getData($code);
  164. if (!empty($coodinate)) {
  165. // $coodinate = $coodinate['lo'] . "," . $coodinate['la'];
  166. Redis::instance()->hSet($key, $code, $coodinate);
  167. }
  168. }
  169. $this->location = $coodinate['location'];
  170. return $coodinate['lo'] . "," . $coodinate['la'];
  171. }
  172. /**
  173. * 获取风向
  174. * @param $degree
  175. * @return mixed
  176. */
  177. private function getWindDirect($degree)
  178. {
  179. $directArr = ["北风", "东北风", "东北风", "东北风", "东风", "东南风", "东南风", "东南风", "南风", "西南风", "西南风", "西南风", "西风", "西北风", "西北风", "西北风"];
  180. $index = 0;
  181. if (348.75 <= $degree && $degree <= 360) {
  182. $index = 0;
  183. } else {
  184. if (0 <= $degree && $degree <= 11.25) {
  185. $index = 0;
  186. } else if (11.25 < $degree && $degree <= 33.75) {
  187. $index = 1;
  188. } else if (33.75 < $degree && $degree <= 56.25) {
  189. $index = 2;
  190. } else if (56.25 < $degree && $degree <= 78.75) {
  191. $index = 3;
  192. } else if (78.75 < $degree && $degree <= 101.25) {
  193. $index = 4;
  194. } else if (101.25 < $degree && $degree <= 123.75) {
  195. $index = 5;
  196. } else if (123.75 < $degree && $degree <= 146.25) {
  197. $index = 6;
  198. } else if (146.25 < $degree && $degree <= 168.75) {
  199. $index = 7;
  200. } else if (168.75 < $degree && $degree <= 191.25) {
  201. $index = 8;
  202. } else if (191.25 < $degree && $degree <= 213.75) {
  203. $index = 9;
  204. } else if (213.75 < $degree && $degree <= 236.25) {
  205. $index = 10;
  206. } else if (236.25 < $degree && $degree <= 258.75) {
  207. $index = 11;
  208. } else if (258.75 < $degree && $degree <= 281.25) {
  209. $index = 12;
  210. } else if (281.25 < $degree && $degree <= 303.75) {
  211. $index = 13;
  212. } else if (303.75 < $degree && $degree <= 326.25) {
  213. $index = 14;
  214. } else if (326.25 < $degree && $degree < 348.75) {
  215. $index = 15;
  216. }
  217. }
  218. return $directArr[$index];
  219. }
  220. private function getWindSpeed($speed)
  221. {
  222. $wind = [
  223. 'name' => '无风',
  224. 'level' => 0
  225. ];
  226. if ($speed > 1 && $speed <= 5) {
  227. $wind = [
  228. 'name' => '软风',
  229. 'level' => 1
  230. ];
  231. }
  232. if ($speed > 5 && $speed <= 11) {
  233. $wind = [
  234. 'name' => '轻风',
  235. 'level' => 2
  236. ];
  237. }
  238. if ($speed > 11 && $speed <= 19) {
  239. $wind = [
  240. 'name' => '微风',
  241. 'level' => 3
  242. ];
  243. }
  244. if ($speed > 19 && $speed <= 28) {
  245. $wind = [
  246. 'name' => '和风',
  247. 'level' => 4
  248. ];
  249. }
  250. if ($speed > 28 && $speed <= 38) {
  251. $wind = [
  252. 'name' => '清风',
  253. 'level' => 5
  254. ];
  255. }
  256. if ($speed > 38 && $speed <= 49) {
  257. $wind = [
  258. 'name' => '强风',
  259. 'level' => 6
  260. ];
  261. }
  262. if ($speed > 49 && $speed <= 61) {
  263. $wind = [
  264. 'name' => '劲风',
  265. 'level' => 7
  266. ];
  267. }
  268. if ($speed > 61 && $speed <= 74) {
  269. $wind = [
  270. 'name' => '大风',
  271. 'level' => 8
  272. ];
  273. }
  274. if ($speed > 74 && $speed <= 88) {
  275. $wind = [
  276. 'name' => '烈风',
  277. 'level' => 9
  278. ];
  279. }
  280. if ($speed > 88 && $speed <= 102) {
  281. $wind = [
  282. 'name' => '狂风',
  283. 'level' => 10
  284. ];
  285. }
  286. if ($speed > 102 && $speed <= 117) {
  287. $wind = [
  288. 'name' => '暴风',
  289. 'level' => 11
  290. ];
  291. }
  292. if ($speed > 117 && $speed <= 134) {
  293. $wind = [
  294. 'name' => '台风',
  295. 'level' => 12
  296. ];
  297. }
  298. if ($speed > 134 && $speed <= 149) {
  299. $wind = [
  300. 'name' => '台风',
  301. 'level' => 13
  302. ];
  303. }
  304. if ($speed > 149 && $speed <= 166) {
  305. $wind = [
  306. 'name' => '台风',
  307. 'level' => 14
  308. ];
  309. }
  310. if ($speed > 166 && $speed <= 183) {
  311. $wind = [
  312. 'name' => '台风',
  313. 'level' => 15
  314. ];
  315. }
  316. if ($speed > 183 && $speed <= 201) {
  317. $wind = [
  318. 'name' => '台风',
  319. 'level' => 16
  320. ];
  321. }
  322. if ($speed > 201 && $speed <= 220) {
  323. $wind = [
  324. 'name' => '台风',
  325. 'level' => 17
  326. ];
  327. }
  328. if ($speed > 220) {
  329. $wind = [
  330. 'name' => '台风',
  331. 'level' => 18
  332. ];
  333. }
  334. return $wind;
  335. }
  336. private function getSkycon($skycon)
  337. {
  338. $arr = [
  339. 'CLEAR_DAY' => '晴',
  340. 'CLEAR_NIGHT' => '晴',
  341. 'PARTLY_CLOUDY_DAY' => '多云',
  342. 'PARTLY_CLOUDY_NIGHT' => '多云',
  343. 'CLOUDY' => '阴',
  344. 'LIGHT_HAZE' => '轻度雾霾',
  345. 'MODERATE_HAZE' => '中度雾霾',
  346. 'HEAVY_HAZE' => '重度雾霾',
  347. 'LIGHT_RAIN' => '小雨',
  348. 'MODERATE_RAIN' => '中雨',
  349. 'HEAVY_RAIN' => '大雨',
  350. 'STORM_RAIN' => '暴雨',
  351. 'FOG' => '雾',
  352. 'LIGHT_SNOW' => '小雪',
  353. 'MODERATE_SNOW' => '中雪',
  354. 'HEAVY_SNOW' => '大雪',
  355. 'STORM_SNOW' => '暴雪',
  356. 'DUST' => '浮尘',
  357. 'SAND' => '沙尘',
  358. 'WIND' => '大风',
  359. 'SLEET' => '雨夹雪',
  360. 'THUNDER_SHOWER' => '雷阵雨',
  361. 'HAIL' => '冰雹',
  362. ];
  363. return $arr[$skycon] ?? "";
  364. }
  365. private function getAlert($code, $type = 1)
  366. {
  367. $alert_type = [
  368. '01' => '台风',
  369. '02' => '暴雨',
  370. '03' => '暴雪',
  371. '04' => '寒潮',
  372. '05' => '⼤风',
  373. '06' => '沙尘暴',
  374. '07' => '高温',
  375. '08' => '干旱',
  376. '09' => '雷电',
  377. '10' => '冰雹',
  378. '11' => '霜冻',
  379. '12' => '大雾',
  380. '13' => '霾',
  381. '14' => '道路结冰',
  382. '15' => '森林火灾',
  383. '16' => '雷雨大风',
  384. ];
  385. $alert_level = [
  386. '01' => '蓝色',
  387. '02' => '黄色',
  388. '03' => '橙色',
  389. '04' => '红色',
  390. ];
  391. $code_1 = substr($code, 0, 2);
  392. $code_2 = substr($code, 2, 2);
  393. if ($type == 1) {
  394. return $code ? $alert_type[$code_1] . $alert_level[$code_2] . '预警' : "";
  395. } else {
  396. return $alert_level[$code_2] ?? "";
  397. }
  398. }
  399. }