{"id":489236,"date":"2026-07-30T19:23:13","date_gmt":"2026-07-30T19:23:13","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=489236"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=489236","title":{"rendered":"\u041d\u0435 \u0432\u044b\u0431\u0438\u0440\u0430\u0439\u0442\u0435 \u043c\u0435\u0436\u0434\u0443 \u042f\u043d\u0434\u0435\u043a\u0441\u043e\u043c \u0438 DIY: \u043a\u0430\u043a \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u00ab\u043c\u043e\u0441\u0442\u00bb \u0434\u043b\u044f \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 \u043d\u0430 PHP \u0437\u0430 \u0432\u0435\u0447\u0435\u0440"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><em>\u0418\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u0443\u043c\u043d\u044b\u0439 \u0434\u043e\u043c \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u0435 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u0430\u00bb, \u0430 \u0430\u043f\u0433\u0440\u0435\u0439\u0434 \u0435\u0433\u043e \u0434\u043e \u0432\u043d\u0435\u0434\u043e\u0440\u043e\u0436\u043d\u0438\u043a\u0430.<\/em><\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435 \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0441\u0442\u0430\u0442\u044c\u044f \u043e \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u0442\u0447\u0438\u043a\u0430\u0445 \u043d\u0430 ESP8266 \u0438\u043b\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430, \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u043e\u043f\u0440\u043e\u0441:<\/p>\n<blockquote>\n<p><em>\u00ab\u0417\u0430\u0447\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434? \u0415\u0441\u0442\u044c \u0436\u0435 \u042f\u043d\u0434\u0435\u043a\u0441 \u0441 \u0410\u043b\u0438\u0441\u043e\u0439, \u0421\u0431\u0435\u0440, Tuya. \u041a\u0443\u043f\u0438\u043b \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0440\u043e\u0437\u0435\u0442\u043a\u0443 \u0437\u0430 500 \u0440\u0443\u0431\u043b\u0435\u0439, \u0438 \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041a\u0430\u043a \u0432\u044b \u0432\u043e\u043e\u0431\u0449\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u043f\u043e\u0434\u0435\u043b\u043a\u0438 \u0432 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b?\u00bb<\/em><\/p>\n<\/blockquote>\n<p>\u0412\u043e\u043f\u0440\u043e\u0441 \u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u044b\u0439. \u041d\u043e \u043e\u043d \u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \u043b\u043e\u0436\u043d\u043e\u0439 \u0434\u0438\u0445\u043e\u0442\u043e\u043c\u0438\u0438: \u0431\u0443\u0434\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0431\u0440\u0430\u0442\u044c <em>\u043b\u0438\u0431\u043e<\/em> \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b, <em>\u043b\u0438\u0431\u043e<\/em> \u0441\u0432\u043e\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0441\u0430\u043c\u0430\u044f \u043c\u043e\u0449\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u2014 <strong>\u0433\u0438\u0431\u0440\u0438\u0434\u043d\u0430\u044f<\/strong>. \u0413\u0434\u0435 \u042f\u043d\u0434\u0435\u043a\u0441 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0435 \u00ab\u0436\u0435\u043b\u0435\u0437\u043e\u00bb (\u0440\u043e\u0437\u0435\u0442\u043a\u0438, \u043b\u0430\u043c\u043f\u044b, \u0440\u0435\u043b\u0435), \u0430 \u0432\u0430\u0448 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u00ab\u043c\u043e\u0437\u0433\u043e\u043c\u00bb, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u043c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u043c \u043d\u0435 \u043f\u043e\u0434 \u0441\u0438\u043b\u0443.<\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c, \u0440\u0430\u0431\u043e\u0447\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435, \u043a\u0430\u043a \u0437\u0430 20 \u043c\u0438\u043d\u0443\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430 PHP \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 (Sensorbox) \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u043e\u0439 \u042f\u043d\u0434\u0435\u043a\u0441. \u0418 \u0434\u0430, \u044d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e.<\/p>\n<h3>\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439: \u00ab\u0423\u043c\u043d\u044b\u0439 \u043f\u043e\u043b\u0438\u0432\u00bb, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043e\u0441\u0438\u043b\u0438\u0442 \u043e\u0431\u043b\u0430\u043a\u043e<\/h3>\n<p>\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443: \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0433\u0440\u044f\u0434\u043a\u0430 \u0441 \u0440\u0430\u0441\u0442\u0435\u043d\u0438\u044f\u043c\u0438.<\/p>\n<ol>\n<li>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0434\u0430\u0442\u0447\u0438\u043a \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b <strong>Sensorbox<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0448\u043b\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440.<\/p>\n<\/li>\n<li>\n<p>\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u0430\u044f <strong>\u0443\u043c\u043d\u0430\u044f \u0440\u043e\u0437\u0435\u0442\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441<\/strong>, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u043d\u0430\u0441\u043e\u0441.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/17c\/a4f\/2cc\/17ca4f2cc159c38b9a4dce854f23ec57.png\" alt=\"\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b\" title=\"\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b\" width=\"1610\" height=\"868\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/17c\/a4f\/2cc\/17ca4f2cc159c38b9a4dce854f23ec57.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/17c\/a4f\/2cc\/17ca4f2cc159c38b9a4dce854f23ec57.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0ef\/d7e\/939\/0efd7e9392338cadb202462eb27410b6.jpg\" alt=\"\u0412 \u0431\u0438\u0434\u043e\u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u043e\u0441, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0439 \u043a \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u0435\" title=\"\u0412 \u0431\u0438\u0434\u043e\u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u043e\u0441, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0439 \u043a \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u0435\" width=\"5712\" height=\"4284\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/0ef\/d7e\/939\/0efd7e9392338cadb202462eb27410b6.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0ef\/d7e\/939\/0efd7e9392338cadb202462eb27410b6.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0412 \u0431\u0438\u0434\u043e\u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u043e\u0441, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0439 \u043a \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u0435<\/figcaption><\/div>\n<\/figure>\n<p><strong>\u0417\u0430\u0434\u0430\u0447\u0430:<\/strong> \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043d\u0430\u0441\u043e\u0441 <em>\u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438<\/em> \u0441\u0440\u0435\u0434\u043d\u044f\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0447\u0432\u044b \u0437\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 3 \u0437\u0430\u043c\u0435\u0440\u0430 \u043d\u0438\u0436\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f.<\/p>\n<p><strong>\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 \u043e\u0431\u043b\u0430\u043a\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430?<\/strong> \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0442\u0430\u043c \u043b\u0438\u043d\u0435\u0439\u043d\u044b: \u00ab\u0415\u0441\u043b\u0438 \u0434\u0430\u0442\u0447\u0438\u043a X \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 Y, \u0441\u0434\u0435\u043b\u0430\u0442\u044c Z\u00bb. \u041e\u043d\u0438 \u043d\u0435 \u0443\u043c\u0435\u044e\u0442:<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 API \u043f\u043e HTTP.<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u043b\u0430\u0442\u044c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0441\u0440\u0435\u0434\u043d\u044f\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439).<\/p>\n<\/li>\n<li>\n<p>\u0413\u0438\u0431\u043a\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0433\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441\u043e\u043c (\u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u043e\u0441 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f\/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u043c\u0438\u043d\u0443\u0442\u0443 \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438).<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0435\u0448\u0435\u043d\u0438\u0435? \u041d\u0430\u0448 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 PHP-\u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0434\u0438\u0440\u0438\u0436\u0435\u0440\u043e\u043c \u044d\u0442\u043e\u0433\u043e \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430, \u0437\u0430 \u0441\u0447\u0435\u0442 \u0447\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.<\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a API \u042f\u043d\u0434\u0435\u043a\u0441\u0430<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0433 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u043e\u0437\u0435\u0442\u043a\u043e\u0439, \u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d \u043a\u043b\u044e\u0447. \u042f\u043d\u0434\u0435\u043a\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 OAuth-\u043f\u0440\u043e\u0446\u0435\u0441\u0441.<\/p>\n<ol>\n<li>\n<p>\u0418\u0434\u0435\u043c \u0432 <a href=\"https:\/\/yandex.ru\/dev\/id\/doc\/ru\/\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441 ID<\/a> \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 \u00ab\u0412\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u044b\u00bb \u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0447\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<\/li>\n<li>\n<p>\u0412 Redirect URI \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c <a href=\"https:\/\/oauth.yandex.ru\/verification_code\" rel=\"noopener noreferrer nofollow\"><code>https:\/\/oauth.yandex.ru\/verification_code<\/code><\/a> (\u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u044d\u0442\u043e \u0441\u0430\u043c\u044b\u0439 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u043b\u044f \u0441\u0432\u043e\u0438\u0445 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432).<\/p>\n<\/li>\n<li>\n<p>\u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u00ab\u0414\u043e\u0441\u0442\u0443\u043f\u044b\u00bb \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0442\u0430\u0432\u0438\u043c \u0433\u0430\u043b\u043e\u0447\u043a\u0438 \u043f\u043e \u043f\u043e\u0438\u0441\u043a\u0443 &#171;smart&#187;  \u0438 &#171;home&#187;.<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c <code>Client ID<\/code> \u0438 <code>Client secret<\/code>.<\/p>\n<\/li>\n<\/ol>\n<figure class=\"full-width \"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/46e\/a59\/776\/46ea5977624f25c3547ba78cf43c7c95.png\" alt=\"\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u043b\u0443\u0436\u0431\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430\" title=\"\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u043b\u0443\u0436\u0431\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430\" width=\"859\" height=\"848\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/46e\/a59\/776\/46ea5977624f25c3547ba78cf43c7c95.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/46e\/a59\/776\/46ea5977624f25c3547ba78cf43c7c95.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u043b\u0443\u0436\u0431\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430<\/figcaption><\/div>\n<\/figure>\n<p><em>\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043e\u043f\u0438\u0441\u0430\u043d \u0432 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438: <\/em><a href=\"https:\/\/yandex.ru\/dev\/dialogs\/smart-home\/doc\/ru\/concepts\/platform-protocol\" rel=\"noopener noreferrer nofollow\"><em>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 \u042f\u043d\u0434\u0435\u043a\u0441\u0430<\/em><\/a><em>.<\/em><\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 2. Hello, Yandex. \u0418\u043b\u0438 \u043f\u0435\u0440\u0435\u0447\u0435\u043d\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0441 \u0438\u0445 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u044f\u043c\u043e \u043d\u0430 \u0412\u0430\u0448\u0435\u043c \u0441\u0430\u0439\u0442\u0435<\/h3>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 cron-\u0441\u043a\u0440\u0438\u043f\u0442\u044b \u2014 \u044d\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438. \u041d\u043e \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u0439\u0442\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u0432\u0441\u0435\u0445 10+ \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u043e\u0437\u0435\u0442\u043a\u0443 \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u044f\u0440\u043a\u043e\u0441\u0442\u044c \u043b\u0430\u043c\u043f\u044b?<\/p>\n<p>\u041f\u0438\u0441\u0430\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0430 React\/Vue \u0438 \u0431\u044d\u043a\u0435\u043d\u0434 \u043d\u0430 Laravel \u2014 \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e. \u041c\u044b \u043f\u043e\u0439\u0434\u0435\u043c \u043f\u0443\u0442\u0435\u043c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0433\u043e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0437\u043c\u0430: <strong>\u043e\u0434\u0438\u043d PHP-\u0444\u0430\u0439\u043b<\/strong>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 SPA-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (Single Page Application).<\/p>\n<p>\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0442\u043e\u0438\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c:<\/p>\n<ol>\n<li>\n<p><strong>\u041e\u043f\u0442\u0438\u043c\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (Optimistic UI).<\/strong> \u041e\u0431\u043b\u0430\u043a\u0430 \u0443\u043c\u043d\u044b\u0445 \u0434\u043e\u043c\u043e\u0432 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0441 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u043e\u0439 1\u20133 \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043a\u0440\u0443\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u043d\u043d\u0435\u0440 \u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043c\u044b \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0442\u0443\u043c\u0431\u043b\u0435\u0440 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0415\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u043c\u044b \u0442\u0430\u043a \u0436\u0435 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u0430\u0442\u0438\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u043e\u0442\u043a\u043b\u0438\u043a \u0437\u0430 0 \u043c\u0441.<\/p>\n<\/li>\n<li>\n<p><strong>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b (<\/strong><code><strong>curl_multi<\/strong><\/code><strong>).<\/strong> \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u043f\u0440\u043e\u0441 10 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0447\u0435\u0440\u0435\u0437 API \u042f\u043d\u0434\u0435\u043a\u0441\u0430 \u0437\u0430\u043d\u044f\u043b \u0431\u044b 15\u201320 \u0441\u0435\u043a\u0443\u043d\u0434. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <code>curl_multi_init<\/code>, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u043d\u0435 \u043e\u0442\u0432\u0435\u0442\u0438\u0432\u0448\u0438\u0435 \u043d\u0430 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u042d\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u043e 2\u20133 \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<\/li>\n<li>\n<p><strong>\u0411\u043e\u0440\u044c\u0431\u0430 \u0441 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0439 PHP.<\/strong> \u041f\u0440\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 PHP \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0444\u0430\u0439\u043b \u0441\u0435\u0441\u0441\u0438\u0438. \u0415\u0441\u043b\u0438 \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 &#171;\u0437\u0430\u0432\u0438\u0441&#187;, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u0441\u0442\u0430\u043d\u0443\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0432\u044b\u0437\u043e\u0432 <code>session_write_close()<\/code> \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0447\u0442\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0432 API-\u0440\u043e\u0443\u0442\u0435\u0440\u0435.<\/p>\n<\/li>\n<li>\n<p><strong>Null-safe \u043f\u0430\u0440\u0441\u0438\u043d\u0433.<\/strong> API \u042f\u043d\u0434\u0435\u043a\u0441\u0430 \u0438\u043d\u043e\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>null<\/code> \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445 \u0438\u043b\u0438 \u043e\u0444\u0444\u043b\u0430\u0439\u043d-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432. \u0416\u0435\u0441\u0442\u043a\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 <code>onCap?.state<\/code> \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0430\u0434\u0435\u043d\u0438\u0435 JavaScript-\u043a\u043e\u0434\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 <code>Cannot read properties of null<\/code>.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0413\u043e\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 (yandex_smarthome.php)<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b <code>yandex_smarthome.php<\/code> \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0432 \u043d\u0435\u0433\u043e \u044d\u0442\u043e\u0442 \u043a\u043e\u0434. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c <code>$CLIENT_ID<\/code> \u0438 <code>$CLIENT_SECRET<\/code> \u043d\u0430 \u0432\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430.<\/p>\n<pre><code class=\"php\">&lt;?php\/** * Technofeya Home: Smart Dashboard (v9.6 - Production Ready) * \u041e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (SPA) \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u042f\u043d\u0434\u0435\u043a\u0441.\u0423\u043c\u043d\u044b\u043c \u0434\u043e\u043c\u043e\u043c. * \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438: Optimistic UI, curl_multi, null-safe parsing, session lock fix. *\/session_start();\/\/ ==================== \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 ====================\/\/ \u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u044e\u0434\u0430 \u0432\u0430\u0448\u0438 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441$CLIENT_ID     = '\u0412\u0410\u0428_CLIENT_ID_\u0418\u0417_\u041a\u041e\u041d\u0421\u041e\u041b\u0418';$CLIENT_SECRET = '\u0412\u0410\u0428_CLIENT_SECRET_\u0418\u0417_\u041a\u041e\u041d\u0421\u041e\u041b\u0418';$REDIRECT_URI  = 'https:\/\/oauth.yandex.ru\/verification_code';$API_BASE      = 'https:\/\/api.iot.yandex.net\/v1.0';\/\/ ===================================================function apiRequest($url, $method = 'GET', $data = null, $headers = []) {    $ch = curl_init($url);    curl_setopt_array($ch, [        CURLOPT_RETURNTRANSFER =&gt; true,        CURLOPT_HTTPHEADER     =&gt; $headers,        CURLOPT_TIMEOUT        =&gt; 10,        CURLOPT_SSL_VERIFYPEER =&gt; true,        CURLOPT_FOLLOWLOCATION =&gt; true,    ]);    if ($method === 'POST') {        curl_setopt($ch, CURLOPT_POST, true);        if (!empty($data)) {            if (strpos($url, 'oauth.yandex.ru') !== false) {                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));            } else {                $headers = array_filter($headers, fn($h) =&gt; strpos($h, 'Content-Type') === false);                $headers[] = 'Content-Type: application\/json';                curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? json_encode($data) : $data);                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);            }        }    }    $response = curl_exec($ch);    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);    curl_close($ch);    $decoded = json_decode($response, true);    return ['code' =&gt; $httpCode, 'body' =&gt; $decoded ?: ['raw_response' =&gt; $response]];}\/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u0443\u0441\u043a\u043e\u0440\u044f\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0432 \u0440\u0430\u0437\u044b)function apiRequestParallel($urls, $headers) {    $mh = curl_multi_init();    $curlArray = [];        foreach ($urls as $key =&gt; $url) {        $ch = curl_init($url);        curl_setopt_array($ch, [            CURLOPT_RETURNTRANSFER =&gt; true,            CURLOPT_HTTPHEADER     =&gt; $headers,            CURLOPT_TIMEOUT        =&gt; 10,            CURLOPT_SSL_VERIFYPEER =&gt; true,        ]);        curl_multi_add_handle($mh, $ch);        $curlArray[$key] = $ch;    }        $running = null;    do { curl_multi_exec($mh, $running); } while ($running &gt; 0);        $results = [];    foreach ($curlArray as $key =&gt; $ch) {        $response = curl_multi_getcontent($ch);        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);        $decoded = json_decode($response, true);        $results[$key] = ['code' =&gt; $httpCode, 'body' =&gt; $decoded ?: ['raw_response' =&gt; $response]];        curl_multi_remove_handle($mh, $ch);    }    curl_multi_close($mh);    return $results;}\/\/ ==================== API ROUTER (\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 AJAX) ====================if (isset($_GET['api']) &amp;&amp; isset($_SESSION['yandex_token'])) {    header('Content-Type: application\/json');    $token = $_SESSION['yandex_token'];        \/\/ \u041a\u0420\u0418\u0422\u0418\u0427\u0415\u0421\u041a\u0418 \u0412\u0410\u0416\u041d\u041e: \u0441\u043d\u0438\u043c\u0430\u0435\u043c \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u0441\u0435\u0441\u0441\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0435 \u0432\u0438\u0441\u0435\u043b\u0438    session_write_close();    $iotHeaders = [\"Authorization: Bearer {$token}\"];    if ($_GET['api'] === 'devices') {        \/\/ 1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432        $infoRes = apiRequest(\"{$API_BASE}\/user\/info\", 'GET', null, $iotHeaders);        if ($infoRes['code'] !== 200 || !isset($infoRes['body']['devices'])) {            echo json_encode(['error' =&gt; '\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432', 'details' =&gt; $infoRes['body']]);            exit;        }                $devices = $infoRes['body']['devices'];        $deviceIds = array_map(fn($d) =&gt; ['id' =&gt; $d['id']], $devices);                \/\/ 2. \u041f\u0440\u043e\u0431\u0443\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439        $queryRes = apiRequest(\"{$API_BASE}\/user\/devices\/query\", 'POST', ['devices' =&gt; $deviceIds], $iotHeaders);        $states = [];                if ($queryRes['code'] === 200 &amp;&amp; isset($queryRes['body']['devices'])) {            foreach ($queryRes['body']['devices'] as $devState) {                $states[$devState['id']] = $devState['capabilities'] ?? [];            }        }                \/\/ 3. Fallback: \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0433\u0434\u0435 \u043d\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f        $missing = array_filter($devices, fn($d) =&gt; !isset($states[$d['id']]));        if (count($missing) &gt; 0) {            $urls = array_map(fn($d) =&gt; \"{$API_BASE}\/devices\/{$d['id']}\", $missing);            $parallelRes = apiRequestParallel($urls, $iotHeaders);                        foreach ($parallelRes as $key =&gt; $res) {                if ($res['code'] === 200 &amp;&amp; isset($res['body']['capabilities'])) {                    $devId = $missing[$key]['id'];                    $states[$devId] = $res['body']['capabilities'];                    $devices[array_search($devId, array_column($devices, 'id'))]['device_state'] = $res['body']['state'] ?? 'unknown';                }            }        }                \/\/ 4. \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430        foreach ($devices as &amp;$dev) {            $dev['current_state'] = $states[$dev['id']] ?? [];            if (!isset($dev['device_state'])) {                $dev['device_state'] = 'unknown';            }        }                echo json_encode(['success' =&gt; true, 'devices' =&gt; $devices]);        exit;    }    if ($_GET['api'] === 'action') {        $rawInput = file_get_contents('php:\/\/input');        $input = json_decode($rawInput, true) ?: $_POST;        if (empty($input) || !isset($input['devices'])) {            echo json_encode(['success' =&gt; false, 'error' =&gt; '\u041f\u0443\u0441\u0442\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435']);            exit;        }        $res = apiRequest(\"{$API_BASE}\/devices\/actions\", 'POST', $input, $iotHeaders);        if ($res['code'] === 200 || $res['code'] === 202) {            echo json_encode(['success' =&gt; true]);        } else {            echo json_encode(['success' =&gt; false, 'error' =&gt; \"HTTP {$res['code']}\", 'details' =&gt; $res['body']]);        }        exit;    }}\/\/ ==================== \u041b\u041e\u0413\u0418\u041a\u0410 \u0410\u0412\u0422\u041e\u0420\u0418\u0417\u0410\u0426\u0418\u0418 ====================if (isset($_POST['manual_code']) &amp;&amp; !isset($_SESSION['yandex_token'])) {    $res = apiRequest('https:\/\/oauth.yandex.ru\/token', 'POST', [        'grant_type' =&gt; 'authorization_code', 'code' =&gt; trim($_POST['manual_code']),        'client_id' =&gt; $CLIENT_ID, 'client_secret' =&gt; $CLIENT_SECRET, 'redirect_uri' =&gt; $REDIRECT_URI    ]);    if ($res['code'] == 200 &amp;&amp; isset($res['body']['access_token'])) {        $_SESSION['yandex_token'] = $res['body']['access_token'];        $userInfo = apiRequest('https:\/\/login.yandex.ru\/info?format=json', 'GET', null, ['Authorization: OAuth ' . $_SESSION['yandex_token']]);        $_SESSION['yandex_user'] = $userInfo['body']['login'] ?? 'User';        header('Location: ' . strtok($_SERVER[\"REQUEST_URI\"], '?'));        exit;    }    $authError = \"\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438: \" . json_encode($res['body']);}if (!isset($_SESSION['yandex_token'])) {    $authUrl = \"https:\/\/oauth.yandex.ru\/authorize?response_type=code&amp;client_id={$CLIENT_ID}&amp;redirect_uri=\" . urlencode($REDIRECT_URI);    die(\"&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta charset='utf-8'&gt;&lt;title&gt;\u0412\u0445\u043e\u0434 \u0432 \u0423\u043c\u043d\u044b\u0439 \u0434\u043e\u043c&lt;\/title&gt;    &lt;style&gt;body{font-family:system-ui;max-width:400px;margin:100px auto;padding:20px;text-align:center;background:#f5f5f7}    .card{background:#fff;padding:30px;border-radius:16px;box-shadow:0 4px 20px rgba(0,0,0,0.08)}    .btn{display:block;width:100%;padding:14px;background:#fc0;color:#000;border:none;border-radius:10px;font-weight:bold;font-size:16px;cursor:pointer;margin:15px 0;text-decoration:none}    input{width:100%;padding:12px;border:1px solid #ddd;border-radius:10px;margin:10px 0;box-sizing:border-box;text-align:center}&lt;\/style&gt;&lt;\/head&gt;    &lt;body&gt;&lt;div class='card'&gt;&lt;h2&gt;\ud83d\udd0c Technofeya Home&lt;\/h2&gt;    \" . (isset($authError) ? \"&lt;p style='color:red;font-size:14px'&gt;$authError&lt;\/p&gt;\" : \"\") . \"    &lt;p style='font-size:14px;color:#666'&gt;1. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0438\u0436\u0435.&lt;br&gt;2. \u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u0435 \u0434\u043e\u0441\u0442\u0443\u043f \u0432 \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.&lt;br&gt;3. \u0421\u043a\u043e\u043f\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u043e\u0434 \u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0435\u0433\u043e \u0441\u044e\u0434\u0430.&lt;\/p&gt;    &lt;a href='$authUrl' target='_blank' class='btn'&gt;\ud83d\udd11 \u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043a\u043e\u0434 \u043e\u0442 \u042f\u043d\u0434\u0435\u043a\u0441\u0430&lt;\/a&gt;    &lt;form method='POST'&gt;&lt;input type='text' name='manual_code' placeholder='\u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u043a\u043e\u0434' required&gt;&lt;button class='btn' style='background:#007aff;color:#fff'&gt;\u0412\u043e\u0439\u0442\u0438&lt;\/button&gt;&lt;\/form&gt;&lt;\/div&gt;&lt;\/body&gt;&lt;\/html&gt;\");}?&gt;&lt;!DOCTYPE html&gt;&lt;html lang=\"ru\"&gt;&lt;head&gt;    &lt;meta charset=\"UTF-8\"&gt;    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;    &lt;title&gt;Technofeya Home Dashboard&lt;\/title&gt;    &lt;!-- \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c Tailwind CSS \u0434\u043b\u044f \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 --&gt;    &lt;script src=\"https:\/\/cdn.tailwindcss.com\"&gt;&lt;\/script&gt;    &lt;style&gt;        .toggle-checkbox:checked { right: 0; border-color: #34c759; }        .toggle-checkbox:checked + .toggle-label { background-color: #34c759; }        .glass-panel { background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }        .loader { border: 3px solid #f3f3f3; border-top: 3px solid #007aff; border-radius: 50%; width: 24px; height: 24px; animation: spin 1s linear infinite; }        @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }    &lt;\/style&gt;&lt;\/head&gt;&lt;body class=\"bg-gray-100 text-gray-800 font-sans min-h-screen\"&gt;    &lt;header class=\"glass-panel sticky top-0 z-50 border-b border-gray-200 shadow-sm\"&gt;        &lt;div class=\"max-w-4xl mx-auto px-4 py-4 flex justify-between items-center\"&gt;            &lt;h1 class=\"text-xl font-bold flex items-center gap-2\"&gt;\ud83d\udd0c Technofeya Home &lt;span class=\"text-xs font-normal text-gray-500 bg-gray-200 px-2 py-1 rounded-full\"&gt;v9.6&lt;\/span&gt;&lt;\/h1&gt;            &lt;div class=\"flex items-center gap-3\"&gt;                &lt;span class=\"text-sm text-gray-600 hidden sm:inline\"&gt;\ud83d\udc64 &lt;?= htmlspecialchars($_SESSION['yandex_user']) ?&gt;&lt;\/span&gt;                &lt;button onclick=\"window.location.href='?action=logout'\" class=\"text-sm text-red-500 hover:text-red-700 font-medium border border-red-200 px-3 py-1 rounded-lg hover:bg-red-50 transition\"&gt;\u0412\u044b\u0439\u0442\u0438&lt;\/button&gt;            &lt;\/div&gt;        &lt;\/div&gt;    &lt;\/header&gt;    &lt;main class=\"max-w-4xl mx-auto px-4 py-8\"&gt;        &lt;div class=\"flex justify-between items-center mb-6\"&gt;            &lt;h2 class=\"text-2xl font-semibold\"&gt;\u041c\u043e\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430&lt;\/h2&gt;            &lt;button onclick=\"loadDevices()\" class=\"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center gap-2 transition shadow-sm\"&gt;                &lt;span id=\"refresh-icon\"&gt;\ud83d\udd04&lt;\/span&gt; \u041e\u0431\u043d\u043e\u0432\u0438\u0442\u044c            &lt;\/button&gt;        &lt;\/div&gt;        &lt;div id=\"devices-grid\" class=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5\"&gt;            &lt;div class=\"col-span-full text-center py-20 text-gray-500\"&gt;                &lt;div class=\"loader mx-auto mb-4\"&gt;&lt;\/div&gt;                \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432...            &lt;\/div&gt;        &lt;\/div&gt;    &lt;\/main&gt;    &lt;div id=\"toast\" class=\"fixed bottom-5 right-5 bg-gray-800 text-white px-6 py-3 rounded-lg shadow-xl transform translate-y-20 opacity-0 transition-all duration-300 z-50 max-w-sm break-words\"&gt;&lt;\/div&gt;    &lt;script&gt;        async function loadDevices() {            const grid = document.getElementById('devices-grid');            const btn = document.getElementById('refresh-icon');            btn.classList.add('animate-spin');                        try {                const res = await fetch('?api=devices');                const data = await res.json();                                if (data.error) {                    grid.innerHTML = `&lt;div class=\"col-span-full bg-red-100 text-red-700 p-4 rounded-lg border border-red-200\"&gt;${data.error}&lt;\/div&gt;`;                    return;                }                grid.innerHTML = '';                if (!data.devices || data.devices.length === 0) {                    grid.innerHTML = `&lt;div class=\"col-span-full text-center text-gray-500 py-10\"&gt;\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u044b.&lt;\/div&gt;`;                    return;                }                data.devices.forEach(dev =&gt; grid.appendChild(createDeviceCard(dev)));                showToast('\u2705 \u0421\u043f\u0438\u0441\u043e\u043a \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d');            } catch (e) {                grid.innerHTML = `&lt;div class=\"col-span-full bg-red-100 text-red-700 p-4 rounded-lg\"&gt;\u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u0435\u0442\u0438: ${e.message}&lt;\/div&gt;`;            } finally {                btn.classList.remove('animate-spin');            }        }        function createDeviceCard(dev) {            const state = dev.current_state || [];            const onCap = state.find(c =&gt; c.type === 'devices.capabilities.on_off');                        \/\/ NULL-SAFE: \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435, \u0434\u0430\u0436\u0435 \u0435\u0441\u043b\u0438 \u042f\u043d\u0434\u0435\u043a\u0441 \u0432\u0435\u0440\u043d\u0443\u043b null            const capState = onCap?.state || {};             const isRetrievable = onCap ? onCap.retrievable : false;            const isOn = isRetrievable ? !!capState.value : false;            const lastUpdated = isRetrievable &amp;&amp; capState.last_updated ? capState.last_updated : 0;            const isStale = lastUpdated &gt; 0 &amp;&amp; (Date.now() \/ 1000 - lastUpdated) &gt; 3600; \/\/ \u0421\u0442\u0430\u0440\u0448\u0435 1 \u0447\u0430\u0441\u0430                        const deviceState = dev.device_state || 'unknown';            const isOffline = deviceState === 'offline';            let statusBadge = '';            if (isOffline) {                statusBadge = `&lt;span class=\"text-[10px] bg-gray-200 text-gray-600 px-2 py-0.5 rounded-full\"&gt;\ud83d\udce1 \u041d\u0435 \u0432 \u0441\u0435\u0442\u0438&lt;\/span&gt;`;            } else if (isStale) {                statusBadge = `&lt;span class=\"text-[10px] bg-yellow-100 text-yellow-700 px-2 py-0.5 rounded-full\" title=\"\u0421\u0442\u0430\u0442\u0443\u0441 \u043d\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u043b\u0441\u044f \u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0430\"&gt;\u26a0\ufe0f \u0423\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435&lt;\/span&gt;`;            } else if (!isRetrievable) {                statusBadge = `&lt;span class=\"text-[10px] bg-blue-100 text-blue-700 px-2 py-0.5 rounded-full\" title=\"\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043d\u0435 \u0441\u043e\u043e\u0431\u0449\u0430\u0435\u0442 \u0441\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u0443\u0441\"&gt;\u2753 \u0421\u0442\u0430\u0442\u0443\u0441 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d&lt;\/span&gt;`;            }            let extraControls = '';            const rangeCap = state.find(c =&gt; c.type === 'devices.capabilities.range');            if (rangeCap &amp;&amp; rangeCap.retrievable &amp;&amp; rangeCap?.state) {                const val = rangeCap.state.value;                const min = rangeCap.parameters?.range?.min || 1;                const max = rangeCap.parameters?.range?.max || 100;                const instance = rangeCap.state.instance;                const label = instance === 'brightness' ? '\u042f\u0440\u043a\u043e\u0441\u0442\u044c' : (instance === 'temperature' ? '\u0422\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0430' : '\u0423\u0440\u043e\u0432\u0435\u043d\u044c');                                extraControls += `                    &lt;div class=\"mt-4 pt-4 border-t border-gray-100\"&gt;                        &lt;div class=\"flex justify-between text-xs text-gray-500 mb-1\"&gt;                            &lt;span&gt;${label}&lt;\/span&gt;                            &lt;span id=\"val-${dev.id}-${instance}\" class=\"font-bold text-blue-600\"&gt;${val}&lt;\/span&gt;                        &lt;\/div&gt;                        &lt;input type=\"range\" min=\"${min}\" max=\"${max}\" value=\"${val}\"                             class=\"w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-500\"                            onchange=\"sendRangeAction('${dev.id}', '${instance}', this.value)\" ${isOffline ? 'disabled' : ''}&gt;                    &lt;\/div&gt;                `;            }            const card = document.createElement('div');            card.id = `card-${dev.id}`;            card.className = `glass-panel rounded-2xl p-5 shadow-sm border border-white\/60 hover:shadow-md transition relative overflow-hidden ${isOffline ? 'opacity-75' : ''}`;            card.innerHTML = `                &lt;div class=\"absolute top-0 right-0 w-24 h-24 bg-gradient-to-br from-blue-50 to-transparent rounded-bl-full -mr-4 -mt-4\"&gt;&lt;\/div&gt;                &lt;div class=\"relative z-10\"&gt;                    &lt;div class=\"flex justify-between items-start mb-2\"&gt;                        &lt;div&gt;                            &lt;h3 class=\"font-bold text-lg text-gray-800 leading-tight flex items-center gap-2\"&gt;                                ${dev.name}                                ${statusBadge}                            &lt;\/h3&gt;                            &lt;p class=\"text-[10px] text-gray-400 mt-1 font-mono truncate w-40\" title=\"${dev.id}\"&gt;${dev.id}&lt;\/p&gt;                        &lt;\/div&gt;                        &lt;div id=\"status-${dev.id}\" class=\"status-indicator w-3 h-3 rounded-full mt-1.5 ${isOn ? 'bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-gray-300'}\"&gt;&lt;\/div&gt;                    &lt;\/div&gt;                                        &lt;div class=\"flex items-center justify-between mt-4\"&gt;                        &lt;span id=\"text-${dev.id}\" class=\"text-sm font-medium ${isOn ? 'text-green-600' : 'text-gray-400'}\"&gt;                            ${isOn ? '\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e' : (isRetrievable ? '\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e' : '\u0421\u0442\u0430\u0442\u0443\u0441 \u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d')}                        &lt;\/span&gt;                                                &lt;label class=\"relative inline-flex items-center cursor-pointer ${isOffline ? 'opacity-50 cursor-not-allowed' : ''}\"&gt;                            &lt;input type=\"checkbox\" id=\"toggle-${dev.id}\" class=\"sr-only toggle-checkbox\" ${isOn ? 'checked' : ''}                                 onchange=\"sendOnOffAction('${dev.id}', this.checked)\" ${isOffline ? 'disabled' : ''}&gt;                            &lt;div class=\"w-11 h-6 bg-gray-200 rounded-full toggle-label transition-colors duration-300\"&gt;&lt;\/div&gt;                            &lt;div id=\"knob-${dev.id}\" class=\"absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform duration-300 transform ${isOn ? 'translate-x-5' : 'translate-x-0'} shadow-sm\"&gt;&lt;\/div&gt;                        &lt;\/label&gt;                    &lt;\/div&gt;                    ${extraControls}                &lt;\/div&gt;            `;            return card;        }        \/\/ \u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u0434\u043e \u043e\u0442\u0432\u0435\u0442\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 (Optimistic UI)        function updateDeviceUI(deviceId, isOn) {            const status = document.getElementById(`status-${deviceId}`);            const text = document.getElementById(`text-${deviceId}`);            const toggle = document.getElementById(`toggle-${deviceId}`);            const knob = document.getElementById(`knob-${deviceId}`);                        if (status) status.className = `status-indicator w-3 h-3 rounded-full mt-1.5 ${isOn ? 'bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-gray-300'}`;            if (text) {                text.className = `text-sm font-medium ${isOn ? 'text-green-600' : 'text-gray-400'}`;                text.textContent = isOn ? '\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u043e' : '\u0412\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u043e';            }            if (toggle) toggle.checked = isOn;            if (knob) knob.className = `absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition-transform duration-300 transform ${isOn ? 'translate-x-5' : 'translate-x-0'} shadow-sm`;        }        async function sendAction(deviceId, payload, optimisticUpdate = null) {            showToast('\u23f3 \u041e\u0442\u043f\u0440\u0430\u0432\u043a\u0430 \u043a\u043e\u043c\u0430\u043d\u0434\u044b...', 'info');            if (optimisticUpdate !== null) updateDeviceUI(deviceId, optimisticUpdate);                        try {                const res = await fetch('?api=action', {                    method: 'POST',                    headers: {'Content-Type': 'application\/json'},                    body: JSON.stringify({devices: [{id: deviceId, actions: [payload]}]})                });                const data = await res.json();                                if (data.success) {                    showToast('\u2705 \u041a\u043e\u043c\u0430\u043d\u0434\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430');                    \/\/ \u0424\u043e\u043d\u043e\u0432\u0430\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 5 \u0441\u0435\u043a\u0443\u043d\u0434, \u043a\u043e\u0433\u0434\u0430 \u043e\u0431\u043b\u0430\u043a\u043e \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442 \u0441\u0442\u0430\u0442\u0443\u0441                    setTimeout(loadDevices, 5000);                } else {                    showToast('\u274c ' + (data.error || '\u041e\u0448\u0438\u0431\u043a\u0430') + (data.details ? ': ' + JSON.stringify(data.details) : ''), 'error');                    \/\/ \u041e\u0442\u043a\u0430\u0442 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 \u043f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435                    if (optimisticUpdate !== null) updateDeviceUI(deviceId, !optimisticUpdate);                }            } catch (e) {                showToast('\u274c \u041e\u0448\u0438\u0431\u043a\u0430 \u0441\u0435\u0442\u0438: ' + e.message, 'error');                if (optimisticUpdate !== null) updateDeviceUI(deviceId, !optimisticUpdate);            }        }        function sendOnOffAction(deviceId, isOn) {            sendAction(deviceId, {type: 'devices.capabilities.on_off', state: {instance: 'on', value: isOn}}, isOn);        }        function sendRangeAction(deviceId, instance, value) {            const el = document.getElementById(`val-${deviceId}-${instance}`);            if (el) el.innerText = value;            sendAction(deviceId, {type: 'devices.capabilities.range', state: {instance: instance, value: parseInt(value)}});        }        function showToast(msg, type = 'success') {            const toast = document.getElementById('toast');            toast.innerText = msg;            toast.className = `fixed bottom-5 right-5 px-6 py-3 rounded-lg shadow-xl transform transition-all duration-300 z-50 text-white text-sm ${type === 'error' ? 'bg-red-500' : (type === 'info' ? 'bg-gray-600' : 'bg-green-600')}`;            toast.style.transform = 'translateY(0)';            toast.style.opacity = '1';            setTimeout(() =&gt; {                toast.style.transform = 'translateY(20px)';                toast.style.opacity = '0';            }, 4000);        }        document.addEventListener('DOMContentLoaded', loadDevices);    &lt;\/script&gt;&lt;\/body&gt;&lt;\/html&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442:<\/h3>\n<ol>\n<li>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0441\u043a\u0440\u0438\u043f\u0442 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u0444\u043e\u0440\u043c\u0443 \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430 \u043a\u043e\u0434\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u0430 \u0432\u044b \u0443\u0432\u0438\u0434\u0438\u0442\u0435 \u0441\u0435\u0442\u043a\u0443 \u043a\u0430\u0440\u0442\u043e\u0447\u0435\u043a \u0432\u0441\u0435\u0445 \u0432\u0430\u0448\u0438\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432.<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043d\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u043b\u044e\u0431\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u0438. \u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f <strong>\u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e<\/strong> (\u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f Optimistic UI), \u043a\u043e\u043c\u0430\u043d\u0434\u0430 \u0443\u0439\u0434\u0435\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0430 \u0447\u0435\u0440\u0435\u0437 5 \u0441\u0435\u043a\u0443\u043d\u0434 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u0442\u0438\u0445\u0430\u044f \u0444\u043e\u043d\u043e\u0432\u0430\u044f \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u043e\u0444\u0444\u043b\u0430\u0439\u043d, \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0430 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043b\u0443\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0439, \u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u0435\u043b\u044c \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0432\u0432\u043e\u0434\u0438\u0442\u044c \u0432\u0430\u0441 \u0432 \u0437\u0430\u0431\u043b\u0443\u0436\u0434\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u0435\u043d, \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0441\u0431\u043e\u0440\u0449\u0438\u043a\u043e\u0432 (Webpack\/Vite) \u0438\u043b\u0438 \u0431\u0430\u0437 \u0434\u0430\u043d\u043d\u044b\u0445, \u0438 \u0433\u043e\u0442\u043e\u0432 \u043a \u0440\u0430\u0431\u043e\u0442\u0435 \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043d\u0430 \u0445\u043e\u0441\u0442\u0438\u043d\u0433.<\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 3. \u041c\u0430\u0433\u0438\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u0445 HTTP-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/h3>\n<p>\u0412\u0441\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0442\u0440\u043e\u0438\u0442\u0441\u044f \u043d\u0430 \u0434\u0432\u0443\u0445 \u0432\u0435\u0449\u0430\u0445:<\/p>\n<ol>\n<li>\n<p>\u041c\u044b \u0437\u0430\u0431\u0438\u0440\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043d\u0430\u0448\u0435\u0433\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 GET-\u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<\/li>\n<li>\n<p>\u041c\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0432 \u042f\u043d\u0434\u0435\u043a\u0441 \u0447\u0435\u0440\u0435\u0437 POST-\u0437\u0430\u043f\u0440\u043e\u0441 \u0441 JSON-\u0442\u0435\u043b\u043e\u043c.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430\u0448 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 Sensorbox. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u043e\u043d \u043e\u0442\u0434\u0430\u0435\u0442 \u0447\u0438\u0441\u0442\u044b\u0439 JSON:<\/p>\n<pre><code>https:\/\/iot.tfeya.ru\/sensorData.php?login=eugene&amp;sensor_id=1&amp;secret=001c0ee0805bd121xxxxxxxxxxxxxeb9ad576&amp;json=1<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><strong>\u041e\u0442\u0432\u0435\u0442:<\/strong><\/p>\n<pre><code>{\"val\":\"50\",\"datetime\":\"2026-05-30 23:06:01\"}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p><em>(\u0414\u043b\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <\/em><code><em>50<\/em><\/code><em> \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0441\u0443\u0445\u043e\u0441\u0442\u044c).<\/em><\/p>\n<hr\/>\n<h3>\u0428\u0430\u0433 4. \u041f\u0440\u0438\u043c\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u0438\u0432\u0430 (PHP)<\/h3>\n<p>\u041d\u0438\u0436\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439, \u043d\u043e \u043f\u043e\u043b\u043d\u043e\u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u043a\u0440\u0438\u043f\u0442 <code>auto_watering.php<\/code>. \u041e\u043d \u0434\u0435\u043b\u0430\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0437\u0430\u0434\u0443\u043c\u0430\u043b\u0438: \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442 \u0434\u0430\u0442\u0447\u0438\u043a (\u0434\u043b\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u044b \u043d\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u043c, \u043d\u043e \u0441\u0443\u0442\u044c \u0412\u044b \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u0442\u0435), \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0438 \u0434\u0435\u0440\u0433\u0430\u0435\u0442 \u0440\u043e\u0437\u0435\u0442\u043a\u0443 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<pre><code class=\"php\">&lt;?php\/** * auto_watering.php - \u0413\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 *\/\/\/ === \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 ===$YANDEX_TOKEN  = '\u0412\u0410\u0428_OAUTH_TOKEN_\u0417\u0414\u0415\u0421\u042c'; \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u0447\u0435\u0440\u0435\u0437 OAuth$DEVICE_ID     = '19aae231-xxxxx-xxxx-.....'; \/\/ ID \u0440\u043e\u0437\u0435\u0442\u043a\u0438 \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u042f\u043d\u0434\u0435\u043a\u0441$SENSOR_URL    = 'https:\/\/iot.tfeya.ru\/sensorData.php?login=eugene&amp;sensor_id=1&amp;secret=00xxxxxxxx121cba99c83eec54f0f43a0eeeb9ad576&amp;json=1';$DRY_THRESHOLD = 15; \/\/ \u041f\u043e\u0440\u043e\u0433 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438, \u043d\u0438\u0436\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0432\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u0438\u0432 (%)\/\/ 1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0430$sensorDataJson = file_get_contents($SENSOR_URL);$sensorData = json_decode($sensorDataJson, true);if (!$sensorData || !isset($sensorData['val'])) {    die(\"\u041e\u0448\u0438\u0431\u043a\u0430: \u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 Sensorbox\");}$currentMoisture = (int)$sensorData['val'];echo \"\u0422\u0435\u043a\u0443\u0449\u0430\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c: {$currentMoisture}%\\n\";\/\/ 2. \u041b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u0438\u043d\u044f\u0442\u0438\u044f \u0440\u0435\u0448\u0435\u043d\u0438\u044f (\u0437\u0434\u0435\u0441\u044c \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0443\u0441\u0440\u0435\u0434\u043d\u0435\u043d\u0438\u0435, \u0433\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441 \u0438 \u0442.\u0434.)$shouldWater = ($currentMoisture &lt; $DRY_THRESHOLD);\/\/ 3. \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0434\u043b\u044f \u042f\u043d\u0434\u0435\u043a\u0441.\u0423\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430\/\/ \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f: https:\/\/yandex.ru\/dev\/dialogs\/smart-home\/doc\/ru\/concepts\/control-api$payload = [    \"devices\" =&gt; [[        \"id\" =&gt; $DEVICE_ID,        \"actions\" =&gt; [[            \"type\" =&gt; \"devices.capabilities.on_off\",            \"state\" =&gt; [                \"instance\" =&gt; \"on\",                \"value\" =&gt; $shouldWater \/\/ true \u0438\u043b\u0438 false            ]        ]]    ]]];\/\/ 4. \u041e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u0432 \u042f\u043d\u0434\u0435\u043a\u0441$ch = curl_init('https:\/\/api.iot.yandex.net\/v1.0\/devices\/actions');curl_setopt_array($ch, [    CURLOPT_RETURNTRANSFER =&gt; true,    CURLOPT_POST =&gt; true,    CURLOPT_POSTFIELDS =&gt; json_encode($payload),    CURLOPT_HTTPHEADER =&gt; [        'Authorization: Bearer ' . $YANDEX_TOKEN,        'Content-Type: application\/json'    ],    CURLOPT_TIMEOUT =&gt; 10]);$response = curl_exec($ch);$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);\/\/ 5. \u0412\u044b\u0432\u043e\u0434 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430if ($httpCode === 200 || $httpCode === 202) {    $actionText = $shouldWater ? \"\u0412\u041a\u041b\u042e\u0427\u0415\u041d (\u043f\u043e\u043b\u0438\u0432)\" : \"\u0412\u042b\u041a\u041b\u042e\u0427\u0415\u041d (\u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u043b\u0430\u0433\u0438)\";    echo \"\u2705 \u0423\u0441\u043f\u0435\u0445! \u0420\u043e\u0437\u0435\u0442\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441 {$actionText}.\\n\";} else {    echo \"\u274c \u041e\u0448\u0438\u0431\u043a\u0430 API \u042f\u043d\u0434\u0435\u043a\u0441\u0430 (HTTP {$httpCode}): {$response}\\n\";}?&gt;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h4>\u041a\u0430\u043a \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c?<\/h4>\n<ol>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442\u0435 \u044d\u0442\u043e\u0442 \u0444\u0430\u0439\u043b \u043d\u0430 \u0441\u0432\u043e\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, <a href=\"http:\/\/iot.tfeya.ru\" rel=\"noopener noreferrer nofollow\"><code>iot.tfeya.ru<\/code><\/a>).<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0435 <strong>Cron<\/strong> \u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u044d\u0442\u043e\u0433\u043e \u0441\u043a\u0440\u0438\u043f\u0442\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043a\u0430\u0436\u0434\u044b\u0435 30 \u043c\u0438\u043d\u0443\u0442: <code>*\/30 * * * * \/usr\/bin\/php \/var\/www\/iot.tfeya.ru\/auto_watering.php &gt;&gt; \/var\/log\/watering.log<\/code><\/p>\n<\/li>\n<\/ol>\n<p>\u0412\u0441\u0451. \u0412\u0430\u0448 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0434\u0430\u0442\u0447\u0438\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043c\u043c\u0435\u0440\u0447\u0435\u0441\u043a\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439.<\/p>\n<hr\/>\n<h3>\u0410 \u0447\u0442\u043e \u043d\u0430\u0441\u0447\u0435\u0442 \u043a\u0440\u0430\u0441\u0438\u0432\u043e\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430?<\/h3>\n<p>\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 cron \u2014 \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u043e \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0437\u0430\u0439\u0442\u0438 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u0438\u043b\u0438 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432. \u041d\u043e \u0435\u0441\u043b\u0438 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u042f\u043d\u0434\u0435\u043a\u0441\u0430 \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043d\u043e \u043f\u043e \u043f\u0440\u0430\u0432\u0438\u043b\u0430\u043c \u0438\u0433\u0440\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441\u0430\u043c\u043e\u0433\u043e \u042f\u043d\u0434\u0435\u043a\u0441\u0430, \u0442\u043e \u043d\u0430 \u0412\u0430\u0448\u0435\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u0432\u043e\u0431\u043e\u0434\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439. \u0418 \u043f\u043e\u043b\u043d\u0430\u044f \u0441\u0432\u043e\u0431\u043e\u0434\u0430 \u043f\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0443 \u0438\u0442\u043e\u0433\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u0412\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435 \u043f\u0440\u0438 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u0438 \u0412\u0430\u0448\u0435\u0433\u043e \u0423\u043c\u043d\u043e\u0433\u043e \u0414\u043e\u043c\u0430.<\/p>\n<p>\u0412\u044b\u0448\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u043e,  \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0430\u0434 \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u0431\u044b\u043b \u0441\u043e\u0437\u0434\u0430\u043d \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 <strong>SPA-\u0434\u0430\u0448\u0431\u043e\u0440\u0434 \u043d\u0430 PHP + Tailwind CSS<\/strong> (\u0432 \u043e\u0434\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435), \u043a\u043e\u0442\u043e\u0440\u044b\u0439:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 OAuth-\u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u042f\u043d\u0434\u0435\u043a\u0441\u0430.<\/p>\n<\/li>\n<li>\n<p>\u041c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e (\u0431\u0435\u0437 \u043f\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b) \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0441\u0442\u0430\u0442\u0443\u0441 \u0432\u0441\u0435\u0445 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <code>curl_multi<\/code> \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0436\u0434\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442\u0430 10 \u0441\u0435\u043a\u0443\u043d\u0434).<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c\/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u043f\u0440\u0438\u0431\u043e\u0440\u044b, \u043c\u0435\u043d\u044f\u0442\u044c \u044f\u0440\u043a\u043e\u0441\u0442\u044c \u043b\u0430\u043c\u043f \u0438 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440\u0443 \u043e\u0431\u043e\u0433\u0440\u0435\u0432\u0430\u0442\u0435\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f <code>offline<\/code> \u0438 <code>retrievable: false<\/code>, \u043d\u0435 \u0432\u0432\u043e\u0434\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0437\u0430\u0431\u043b\u0443\u0436\u0434\u0435\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p><em>\u041a\u043e\u0434 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u044b\u043c \u0434\u043b\u044f \u0441\u0442\u0430\u0442\u044c\u0438, \u043d\u043e \u043e\u043d \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0433\u043e\u0442\u043e\u0432 \u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e. \u0415\u0441\u043b\u0438 \u0442\u0435\u043c\u0430 \u0431\u0443\u0434\u0435\u0442 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0443, \u044f \u0441 \u0440\u0430\u0434\u043e\u0441\u0442\u044c\u044e \u0432\u044b\u043b\u043e\u0436\u0443 \u0435\u0433\u043e \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u043d\u0430 GitHub \u0438\u043b\u0438 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435.<\/em><\/p>\n<p><em>(\u0421\u043f\u043e\u0439\u043b\u0435\u0440: \u043a\u043b\u044e\u0447 \u043a \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0442\u0430\u043a\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 <\/em><code><em>session_write_close()<\/em><\/code><em> \u043f\u0435\u0440\u0435\u0434 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u0432 PHP \u0438 &#171;\u043e\u043f\u0442\u0438\u043c\u0438\u0441\u0442\u0438\u0447\u043d\u043e\u0435&#187; \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 UI \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 JavaScript, \u043e \u0447\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u0445 \u043f\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0435 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439).<\/em><\/p>\n<hr\/>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043b\u0443\u0447\u0448\u0435, \u0447\u0435\u043c \u00ab\u043f\u0440\u043e\u0441\u0442\u043e \u043a\u0443\u043f\u0438\u0442\u044c \u0432\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e\u0435\u00bb?<\/h3>\n<ol>\n<li>\n<p><strong>\u0421\u0432\u043e\u0431\u043e\u0434\u0430 \u043b\u043e\u0433\u0438\u043a\u0438.<\/strong> \u0412\u044b \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u043e\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u042f\u043d\u0434\u0435\u043a\u0441\u0430. \u0425\u043e\u0442\u0438\u0442\u0435 \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u0432\u0435\u0442, \u0435\u0441\u043b\u0438 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c &lt; 20%, \u0418 \u043d\u0430 \u0443\u043b\u0438\u0446\u0435 \u0442\u0435\u043c\u043d\u043e (\u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e Weather API), \u0418 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0435 \u0432\u0442\u043e\u0440\u043d\u0438\u043a? \u041d\u0430 PHP \u044d\u0442\u043e 5 \u0441\u0442\u0440\u043e\u0447\u0435\u043a \u043a\u043e\u0434\u0430.<\/p>\n<\/li>\n<li>\n<p><strong>\u041e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c.<\/strong> \u041d\u0430 \u043e\u0434\u043d\u043e\u043c \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0435 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430 \u042f\u043d\u0434\u0435\u043a\u0441\u0430, \u0434\u0430\u0442\u0447\u0438\u043a\u0438 Sensorbox, \u043a\u0430\u043c\u0435\u0440\u044b Xiaomi \u0438 \u0440\u0435\u043b\u0435, \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u043c\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 MQTT. \u0412\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u2014 \u044d\u0442\u043e \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a.<\/p>\n<\/li>\n<li>\n<p><strong>\u041d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c.<\/strong> \u0415\u0441\u043b\u0438 \u0437\u0430\u0432\u0442\u0440\u0430 \u042f\u043d\u0434\u0435\u043a\u0441 \u0438\u0437\u043c\u0435\u043d\u0438\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0445 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u0432 \u0438\u043b\u0438 \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u044b, \u0432\u0430\u0448\u0430 \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p><strong>\u042d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e.<\/strong> \u041a\u0430\u043a \u043c\u044b \u0432\u0438\u0434\u0435\u043b\u0438 \u0432\u044b\u0448\u0435, \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043a \u0434\u0432\u0443\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c: <code>file_get_contents()<\/code> \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0438 <code>curl<\/code> \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u0431\u0440\u043e\u043a\u0435\u0440\u043e\u0432 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0438\u043b\u0438 Docker-\u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u043e\u0432 \u0434\u043b\u044f \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f.<\/p>\n<\/li>\n<\/ol>\n<h3>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h3>\n<p>\u041d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c DIY \u0438 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043a\u0430\u043a \u0432\u0440\u0430\u0433\u043e\u0432. \u041e\u043d\u0438 \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0430. \u042f\u043d\u0434\u0435\u043a\u0441 \u0434\u0430\u0435\u0442 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0435, \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 &#171;\u0436\u0435\u043b\u0435\u0437\u043e&#187; (\u0440\u043e\u0437\u0435\u0442\u043a\u0438, \u0440\u0435\u043b\u0435), \u0430 \u0432\u0430\u0448 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0434\u0430\u0435\u0442 \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442, \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u044c \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u043d\u0430\u0434 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<\/p>\n<p>\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0439 \u00ab\u043c\u043e\u0441\u0442\u00bb. \u0412\u044b \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u0441\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043c\u043e\u0449\u043d\u044b\u043c \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u0432\u0430\u0448 \u0443\u043c\u043d\u044b\u0439 \u0434\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u043d\u0435\u0442\u0435 \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0438 \u0441\u0442\u0430\u043d\u0435\u0442\u0435 \u0435\u0433\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u043e\u0440\u043e\u043c.<\/p>\n<hr\/>\n<p><strong>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0441\u0441\u044b\u043b\u043a\u0438:<\/strong><\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/yandex.ru\/dev\/id\/doc\/ru\/\" rel=\"noopener noreferrer nofollow\">\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u042f\u043d\u0434\u0435\u043a\u0441 ID (OAuth)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/yandex.ru\/dev\/dialogs\/smart-home\/doc\/ru\/concepts\/platform-protocol\" rel=\"noopener noreferrer nofollow\">\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u042f\u043d\u0434\u0435\u043a\u0441.\u0423\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/habr.com\/ru\/articles\/789200\/\" rel=\"noopener noreferrer nofollow\">\u0421\u0442\u0430\u0442\u044c\u044f \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043f\u0440\u0435\u0434\u0448\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u0438\u043a\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u043b\u043e<\/a><\/p>\n<\/li>\n<\/ul>\n<hr\/>\n<p><em>P.S. \u0415\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u043e\u043f\u0440\u043e\u0441\u044b \u043f\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435 OAuth \u0438\u043b\u0438 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u043e\u0439 \u043f\u043e\u043b\u0437\u0443\u043d\u043a\u043e\u0432 \u044f\u0440\u043a\u043e\u0441\u0442\u0438 \u0438 \u0442\u0435\u043c\u043f\u0435\u0440\u0430\u0442\u0443\u0440 \u2014 \u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438, \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u044e\u0441\u044c \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c!<\/em><\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1065084\/\">https:\/\/habr.com\/ru\/articles\/1065084\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0418\u043b\u0438 \u043f\u043e\u0447\u0435\u043c\u0443 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u044b\u0439 \u0443\u043c\u043d\u044b\u0439 \u0434\u043e\u043c \u2014 \u044d\u0442\u043e \u043d\u0435 \u00ab\u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u0435 \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434\u0430\u00bb, \u0430 \u0430\u043f\u0433\u0440\u0435\u0439\u0434 \u0435\u0433\u043e \u0434\u043e \u0432\u043d\u0435\u0434\u043e\u0440\u043e\u0436\u043d\u0438\u043a\u0430.\u041a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043d\u0430 \u0425\u0430\u0431\u0440\u0435 \u0432\u044b\u0445\u043e\u0434\u0438\u0442 \u0441\u0442\u0430\u0442\u044c\u044f \u043e \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u0442\u0447\u0438\u043a\u0430\u0445 \u043d\u0430 ESP8266 \u0438\u043b\u0438 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430, \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u043e\u043f\u0440\u043e\u0441:\u00ab\u0417\u0430\u0447\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0435\u0442\u0430\u0442\u044c \u0432\u0435\u043b\u043e\u0441\u0438\u043f\u0435\u0434? \u0415\u0441\u0442\u044c \u0436\u0435 \u042f\u043d\u0434\u0435\u043a\u0441 \u0441 \u0410\u043b\u0438\u0441\u043e\u0439, \u0421\u0431\u0435\u0440, Tuya. \u041a\u0443\u043f\u0438\u043b \u0433\u043e\u0442\u043e\u0432\u0443\u044e \u0440\u043e\u0437\u0435\u0442\u043a\u0443 \u0437\u0430 500 \u0440\u0443\u0431\u043b\u0435\u0439, \u0438 \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041a\u0430\u043a \u0432\u044b \u0432\u043e\u043e\u0431\u0449\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442\u0435\u0441\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u043f\u043e\u0434\u0435\u043b\u043a\u0438 \u0432 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b?\u00bb\u0412\u043e\u043f\u0440\u043e\u0441 \u0441\u043f\u0440\u0430\u0432\u0435\u0434\u043b\u0438\u0432\u044b\u0439. \u041d\u043e \u043e\u043d \u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0438\u0437 \u043b\u043e\u0436\u043d\u043e\u0439 \u0434\u0438\u0445\u043e\u0442\u043e\u043c\u0438\u0438: \u0431\u0443\u0434\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043b\u0438\u0431\u043e \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u0435 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u044b, \u043b\u0438\u0431\u043e \u0441\u0432\u043e\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u0441\u0430\u043c\u0430\u044f \u043c\u043e\u0449\u043d\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u2014 \u0433\u0438\u0431\u0440\u0438\u0434\u043d\u0430\u044f. \u0413\u0434\u0435 \u042f\u043d\u0434\u0435\u043a\u0441 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0437\u0430 \u043d\u0430\u0434\u0435\u0436\u043d\u043e\u0435 \u00ab\u0436\u0435\u043b\u0435\u0437\u043e\u00bb (\u0440\u043e\u0437\u0435\u0442\u043a\u0438, \u043b\u0430\u043c\u043f\u044b, \u0440\u0435\u043b\u0435), \u0430 \u0432\u0430\u0448 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u00ab\u043c\u043e\u0437\u0433\u043e\u043c\u00bb, \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0449\u0438\u043c \u0441\u043b\u043e\u0436\u043d\u044b\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u043b\u0430\u0447\u043d\u044b\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044f\u043c \u043d\u0435 \u043f\u043e\u0434 \u0441\u0438\u043b\u0443.\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c, \u0440\u0430\u0431\u043e\u0447\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435, \u043a\u0430\u043a \u0437\u0430 20 \u043c\u0438\u043d\u0443\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430 PHP \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0430\u0442\u0447\u0438\u043a\u0430 \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 (Sensorbox) \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u043e\u0439 \u042f\u043d\u0434\u0435\u043a\u0441. \u0418 \u0434\u0430, \u044d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e.\u0421\u0446\u0435\u043d\u0430\u0440\u0438\u0439: \u00ab\u0423\u043c\u043d\u044b\u0439 \u043f\u043e\u043b\u0438\u0432\u00bb, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043e\u0441\u0438\u043b\u0438\u0442 \u043e\u0431\u043b\u0430\u043a\u043e\u041f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0437\u0430\u0434\u0430\u0447\u0443: \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0433\u0440\u044f\u0434\u043a\u0430 \u0441 \u0440\u0430\u0441\u0442\u0435\u043d\u0438\u044f\u043c\u0438.\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0434\u0430\u0442\u0447\u0438\u043a \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b Sensorbox, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0448\u043b\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0430 \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440.\u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u0430\u044f \u0443\u043c\u043d\u0430\u044f \u0440\u043e\u0437\u0435\u0442\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u0432\u043e\u0434\u044f\u043d\u043e\u0439 \u043d\u0430\u0441\u043e\u0441.\u0414\u0430\u0442\u0447\u0438\u043a \u0434\u043b\u044f \u0438\u0437\u043c\u0435\u0440\u0435\u043d\u0438\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u0447\u0432\u044b\u0412 \u0431\u0438\u0434\u043e\u043d\u0435 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430\u0441\u043e\u0441, \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u043d\u044b\u0439 \u043a \u0443\u043c\u043d\u043e\u0439 \u0440\u043e\u0437\u0435\u0442\u043a\u0435\u0417\u0430\u0434\u0430\u0447\u0430: \u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043d\u0430\u0441\u043e\u0441 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0441\u0440\u0435\u0434\u043d\u044f\u044f \u0432\u043b\u0430\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u0447\u0432\u044b \u0437\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 3 \u0437\u0430\u043c\u0435\u0440\u0430 \u043d\u0438\u0436\u0435 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f.\u041f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432 \u043e\u0431\u043b\u0430\u043a\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430? \u041f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0442\u0430\u043c \u043b\u0438\u043d\u0435\u0439\u043d\u044b: \u00ab\u0415\u0441\u043b\u0438 \u0434\u0430\u0442\u0447\u0438\u043a X \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 Y, \u0441\u0434\u0435\u043b\u0430\u0442\u044c Z\u00bb. \u041e\u043d\u0438 \u043d\u0435 \u0443\u043c\u0435\u044e\u0442:\u0417\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0445 API \u043f\u043e HTTP.\u0414\u0435\u043b\u0430\u0442\u044c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0443\u0441\u0440\u0435\u0434\u043d\u044f\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439).\u0413\u0438\u0431\u043a\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0433\u0438\u0441\u0442\u0435\u0440\u0435\u0437\u0438\u0441\u043e\u043c (\u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0441\u043e\u0441 \u043d\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f\/\u0432\u044b\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f \u043a\u0430\u0436\u0434\u0443\u044e \u043c\u0438\u043d\u0443\u0442\u0443 \u043d\u0430 \u0433\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0438).\u0420\u0435\u0448\u0435\u043d\u0438\u0435? \u041d\u0430\u0448 \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 PHP-\u0441\u043a\u0440\u0438\u043f\u0442 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u0434\u0438\u0440\u0438\u0436\u0435\u0440\u043e\u043c \u044d\u0442\u043e\u0433\u043e \u043e\u0440\u043a\u0435\u0441\u0442\u0440\u0430, \u0437\u0430 \u0441\u0447\u0435\u0442 \u0447\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u043e \u0434\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438.\u0428\u0430\u0433 1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u043e\u0441\u0442\u0443\u043f \u043a API \u042f\u043d\u0434\u0435\u043a\u0441\u0430\u0427\u0442\u043e\u0431\u044b \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0433 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u043e\u0437\u0435\u0442\u043a\u043e\u0439, \u0435\u043c\u0443 \u043d\u0443\u0436\u0435\u043d \u043a\u043b\u044e\u0447. \u042f\u043d\u0434\u0435\u043a\u0441 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0439 OAuth-\u043f\u0440\u043e\u0446\u0435\u0441\u0441.\u0418\u0434\u0435\u043c \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441 ID \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.\u0412\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 \u00ab\u0412\u0435\u0431-\u0441\u0435\u0440\u0432\u0438\u0441\u044b\u00bb \u0438 \u0432\u044b\u0431\u0438\u0440\u0430\u0435\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442, \u0447\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438.\u0412 Redirect URI \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c https:\/\/oauth.yandex.ru\/verification_code (\u0434\u043b\u044f \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u044d\u0442\u043e \u0441\u0430\u043c\u044b\u0439 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0434\u043b\u044f \u0441\u0432\u043e\u0438\u0445 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432).\u0412 \u0440\u0430\u0437\u0434\u0435\u043b\u0435 \u00ab\u0414\u043e\u0441\u0442\u0443\u043f\u044b\u00bb \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0442\u0430\u0432\u0438\u043c \u0433\u0430\u043b\u043e\u0447\u043a\u0438 \u043f\u043e \u043f\u043e\u0438\u0441\u043a\u0443 &#171;smart&#187;  \u0438 &#171;home&#187;.\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c Client ID \u0438 Client secret.\u041d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043d\u043e\u0432\u043e\u0433\u043e \u0412\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432 \u0441\u043b\u0443\u0436\u0431\u0435 \u042f\u043d\u0434\u0435\u043a\u0441\u0430\u041f\u043e\u0434\u0440\u043e\u0431\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u043c\u0438 \u043e\u043f\u0438\u0441\u0430\u043d \u0432 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438: \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0443\u043c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430 \u042f\u043d\u0434\u0435\u043a\u0441\u0430.\u0428\u0430\u0433 2. Hello, Yandex. \u0418\u043b\u0438 \u043f\u0435\u0440\u0435\u0447\u0435\u043d\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0441 \u0438\u0445 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043f\u0440\u044f\u043c\u043e \u043d\u0430 \u0412\u0430\u0448\u0435\u043c \u0441\u0430\u0439\u0442\u0435\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 cron-\u0441\u043a\u0440\u0438\u043f\u0442\u044b \u2014 \u044d\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u0444\u043e\u043d\u043e\u0432\u043e\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438. \u041d\u043e \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0437\u0430\u0439\u0442\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u0441\u0442\u0430\u0442\u0443\u0441 \u0432\u0441\u0435\u0445 10+ \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0438 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u043e\u0437\u0435\u0442\u043a\u0443 \u0438\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u044f\u0440\u043a\u043e\u0441\u0442\u044c \u043b\u0430\u043c\u043f\u044b?\u041f\u0438\u0441\u0430\u0442\u044c \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434 \u043d\u0430 React\/Vue \u0438 \u0431\u044d\u043a\u0435\u043d\u0434 \u043d\u0430 Laravel \u2014 \u0438\u0437\u0431\u044b\u0442\u043e\u0447\u043d\u043e. \u041c\u044b \u043f\u043e\u0439\u0434\u0435\u043c \u043f\u0443\u0442\u0435\u043c \u044d\u043b\u0435\u0433\u0430\u043d\u0442\u043d\u043e\u0433\u043e \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u0438\u0437\u043c\u0430: \u043e\u0434\u0438\u043d PHP-\u0444\u0430\u0439\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 SPA-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (Single Page Application).\u041f\u0440\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u044d\u0442\u043e\u0433\u043e \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447, \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u0442\u043e\u0438\u0442 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c:\u041e\u043f\u0442\u0438\u043c\u0438\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 (Optimistic UI). \u041e\u0431\u043b\u0430\u043a\u0430 \u0443\u043c\u043d\u044b\u0445 \u0434\u043e\u043c\u043e\u0432 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u0441 \u0437\u0430\u0434\u0435\u0440\u0436\u043a\u043e\u0439 1\u20133 \u0441\u0435\u043a\u0443\u043d\u0434\u044b. \u0412\u043c\u0435\u0441\u0442\u043e \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u043a\u0440\u0443\u0442\u0438\u0442\u044c \u0441\u043f\u0438\u043d\u043d\u0435\u0440 \u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043c\u044b \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0442\u0443\u043c\u0431\u043b\u0435\u0440 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0415\u0441\u043b\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u0435\u0440\u043d\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443, \u043c\u044b \u0442\u0430\u043a \u0436\u0435 \u043c\u0433\u043d\u043e\u0432\u0435\u043d\u043d\u043e \u043e\u0442\u043a\u0430\u0442\u0438\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043e\u0431\u0440\u0430\u0442\u043d\u043e. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432\u0438\u0434\u0438\u0442 \u043e\u0442\u043a\u043b\u0438\u043a \u0437\u0430 0 \u043c\u0441.\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b (curl_multi). \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u043f\u0440\u043e\u0441 10 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432 \u0447\u0435\u0440\u0435\u0437 API \u042f\u043d\u0434\u0435\u043a\u0441\u0430 \u0437\u0430\u043d\u044f\u043b \u0431\u044b 15\u201320 \u0441\u0435\u043a\u0443\u043d\u0434. \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c curl_multi_init, \u0447\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u043d\u0435 \u043e\u0442\u0432\u0435\u0442\u0438\u0432\u0448\u0438\u0435 \u043d\u0430 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441, \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e. \u042d\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u043e 2\u20133 \u0441\u0435\u043a\u0443\u043d\u0434.\u0411\u043e\u0440\u044c\u0431\u0430 \u0441 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u043e\u0439 \u0441\u0435\u0441\u0441\u0438\u0439 PHP. \u041f\u0440\u0438 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0445 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445 PHP \u0431\u043b\u043e\u043a\u0438\u0440\u0443\u0435\u0442 \u0444\u0430\u0439\u043b \u0441\u0435\u0441\u0441\u0438\u0438. \u0415\u0441\u043b\u0438 \u043e\u0434\u0438\u043d \u0437\u0430\u043f\u0440\u043e\u0441 &#171;\u0437\u0430\u0432\u0438\u0441&#187;, \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u0441\u0442\u0430\u043d\u0443\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c. \u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0432\u044b\u0437\u043e\u0432 session_write_close() \u0441\u0440\u0430\u0437\u0443 \u043f\u043e\u0441\u043b\u0435 \u0447\u0442\u0435\u043d\u0438\u044f \u0442\u043e\u043a\u0435\u043d\u0430 \u0432 API-\u0440\u043e\u0443\u0442\u0435\u0440\u0435.Null-safe \u043f\u0430\u0440\u0441\u0438\u043d\u0433. API \u042f\u043d\u0434\u0435\u043a\u0441\u0430 \u0438\u043d\u043e\u0433\u0434\u0430 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 null \u0432\u043c\u0435\u0441\u0442\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u043e\u0432\u044b\u0445 \u0438\u043b\u0438 \u043e\u0444\u0444\u043b\u0430\u0439\u043d-\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432. \u0416\u0435\u0441\u0442\u043a\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 onCap?.state \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0430\u0434\u0435\u043d\u0438\u0435 JavaScript-\u043a\u043e\u0434\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 Cannot read properties of null.\u0413\u043e\u0442\u043e\u0432\u044b\u0439 \u043a\u043e\u0434 \u0434\u0430\u0448\u0431\u043e\u0440\u0434\u0430 (yandex_smarthome.php)\u0421\u043e\u0437\u0434\u0430\u0439\u0442\u0435 \u0444\u0430\u0439\u043b yandex_smarthome.php \u043d\u0430 \u0432\u0430\u0448\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0432 \u043d\u0435\u0433\u043e \u044d\u0442\u043e\u0442 \u043a\u043e\u0434. \u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c $CLIENT_ID \u0438 $CLIENT_SECRET \u043d\u0430 \u0432\u0430\u0448\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430.&lt;?php\/** * Technofeya Home: Smart Dashboard (v9.6 &#8212; Production Ready) * \u041e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 (SPA) \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u042f\u043d\u0434\u0435\u043a\u0441.\u0423\u043c\u043d\u044b\u043c \u0434\u043e\u043c\u043e\u043c. * \u041e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u0438: Optimistic UI, curl_multi, null-safe parsing, session lock fix. *\/session_start();\/\/ ==================== \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 ====================\/\/ \u0412\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0441\u044e\u0434\u0430 \u0432\u0430\u0448\u0438 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u043a\u043e\u043d\u0441\u043e\u043b\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430 \u042f\u043d\u0434\u0435\u043a\u0441$CLIENT_ID     = &#8216;\u0412\u0410\u0428_CLIENT_ID_\u0418\u0417_\u041a\u041e\u041d\u0421\u041e\u041b\u0418&#8217;;$CLIENT_SECRET = &#8216;\u0412\u0410\u0428_CLIENT_SECRET_\u0418\u0417_\u041a\u041e\u041d\u0421\u041e\u041b\u0418&#8217;;$REDIRECT_URI  = &#8216;https:\/\/oauth.yandex.ru\/verification_code&#8217;;$API_BASE      = &#8216;https:\/\/api.iot.yandex.net\/v1.0&#8217;;\/\/ ===================================================function apiRequest($url, $method = &#8216;GET&#8217;, $data = null, $headers = []) {    $ch = curl_init($url);    curl_setopt_array($ch, [        CURLOPT_RETURNTRANSFER =&gt; true,        CURLOPT_HTTPHEADER     =&gt; $headers,        CURLOPT_TIMEOUT        =&gt; 10,        CURLOPT_SSL_VERIFYPEER =&gt; true,        CURLOPT_FOLLOWLOCATION =&gt; true,    ]);    if ($method === &#8216;POST&#8217;) {        curl_setopt($ch, CURLOPT_POST, true);        if (!empty($data)) {            if (strpos($url, &#8216;oauth.yandex.ru&#8217;) !== false) {                curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));            } else {                $headers = array_filter($headers, fn($h) =&gt; strpos($h, &#8216;Content-Type&#8217;) === false);                $headers[] = &#8216;Content-Type: application\/json&#8217;;                curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? json_encode($data) : $data);                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);            }        }    }    $response = curl_exec($ch);    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);    curl_close($ch);    $decoded = json_decode($response, true);    return [&#8216;code&#8217; =&gt; $httpCode, &#8216;body&#8217; =&gt; $decoded ?: [&#8216;raw_response&#8217; =&gt; $response]];}\/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 (\u0443\u0441\u043a\u043e\u0440\u044f\u0435\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439 \u0432 \u0440\u0430\u0437\u044b)function apiRequestParallel($urls, $headers) {    $mh = curl_multi_init();    $curlArray = [];        foreach ($urls as $key =&gt; $url) {        $ch = curl_init($url);        curl_setopt_array($ch, [            CURLOPT_RETURNTRANSFER =&gt; true,            CURLOPT_HTTPHEADER     =&gt; $headers,            CURLOPT_TIMEOUT        =&gt; 10,            CURLOPT_SSL_VERIFYPEER =&gt; true,        ]);        curl_multi_add_handle($mh, $ch);        $curlArray[$key] = $ch;    }        $running = null;    do { curl_multi_exec($mh, $running); } while ($running &gt; 0);        $results = [];    foreach ($curlArray as $key =&gt; $ch) {        $response = curl_multi_getcontent($ch);        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);        $decoded = json_decode($response, true);        $results[$key] = [&#8216;code&#8217; =&gt; $httpCode, &#8216;body&#8217; =&gt; $decoded ?: [&#8216;raw_response&#8217; =&gt; $response]];        curl_multi_remove_handle($mh, $ch);    }    curl_multi_close($mh);    return $results;}\/\/ ==================== API ROUTER (\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 AJAX) ====================if (isset($_GET[&#8216;api&#8217;]) &amp;&amp; isset($_SESSION[&#8216;yandex_token&#8217;])) {    header(&#8216;Content-Type: application\/json&#8217;);    $token = $_SESSION[&#8216;yandex_token&#8217;];        \/\/ \u041a\u0420\u0418\u0422\u0418\u0427\u0415\u0421\u041a\u0418 \u0412\u0410\u0416\u041d\u041e: \u0441\u043d\u0438\u043c\u0430\u0435\u043c \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0443 \u0441\u0435\u0441\u0441\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0435 AJAX-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0435 \u0432\u0438\u0441\u0435\u043b\u0438    session_write_close();    $iotHeaders = [&#171;Authorization: Bearer {$token}&#187;];    if ($_GET[&#8216;api&#8217;] === &#8216;devices&#8217;) {        \/\/ 1. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432        $infoRes = apiRequest(&#171;{$API_BASE}\/user\/info&#187;, &#8216;GET&#8217;, null, $iotHeaders);        if ($infoRes[&#8216;code&#8217;] !== 200 || !isset($infoRes[&#8216;body&#8217;][&#8216;devices&#8217;])) {            echo json_encode([&#8216;error&#8217; =&gt; &#8216;\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432&#8217;, &#8216;details&#8217; =&gt; $infoRes[&#8216;body&#8217;]]);            exit;        }                $devices = $infoRes[&#8216;body&#8217;][&#8216;devices&#8217;];        $deviceIds = array_map(fn($d) =&gt; [&#8216;id&#8217; =&gt; $d[&#8216;id&#8217;]], $devices);                \/\/ 2. \u041f\u0440\u043e\u0431\u0443\u0435\u043c \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u043c\u0430\u0441\u0441\u043e\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439        $queryRes = apiRequest(&#171;{$API_BASE}\/user\/devices\/query&#187;, &#8216;POST&#8217;, [&#8216;devices&#8217; =&gt; $deviceIds], $iotHeaders);        $states = [];                if ($queryRes[&#8216;code&#8217;] === 200 &amp;&amp; isset($queryRes[&#8216;body&#8217;][&#8216;devices&#8217;])) {            foreach ($queryRes[&#8216;body&#8217;][&#8216;devices&#8217;] as $devState) {                $states[$devState[&#8216;id&#8217;]] = $devState[&#8216;capabilities&#8217;] ?? [];            }        }                \/\/ 3. Fallback: \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0435 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0433\u0434\u0435 \u043d\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f        $missing = array_filter($devices, fn($d) =&gt; !isset($states[$d[&#8216;id&#8217;]]));        if (count($missing) &gt; 0) {            $urls = array_map(fn($d) =&gt; &#171;{$API_BASE}\/devices\/{$d[&#8216;id&#8217;]}&#187;, $missing);            $parallelRes = apiRequestParallel($urls, $iotHeaders);                        foreach ($parallelRes as $key =&gt; $res) {                if ($res[&#8216;code&#8217;] === 200 &amp;&amp; isset($res[&#8216;body&#8217;][&#8216;capabilities&#8217;])) {                    $devId = $missing[$key][&#8216;id&#8217;];                    $states[$devId] = $res[&#8216;body&#8217;][&#8216;capabilities&#8217;];                    $devices[array_search($devId, array_column($devices, &#8216;id&#8217;))][&#8216;device_state&#8217;] = $res[&#8216;body&#8217;][&#8216;state&#8217;] ?? &#8216;unknown&#8217;;                }            }        }                \/\/ 4. \u041e\u0431\u044a\u0435\u0434\u0438\u043d\u044f\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0430        foreach ($devices as &amp;$dev) {            $dev[&#8216;current_state&#8217;] = $states[$dev[&#8216;id&#8217;]] ?? [];            if (!isset($dev[&#8216;device_state&#8217;])) {                $dev[&#8216;device_state&#8217;] = &#8216;unknown&#8217;;            }      &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-489236","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489236","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=489236"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/489236\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=489236"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=489236"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=489236"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}