diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2022-07-18 16:29:20 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2022-07-18 16:29:20 +0300 |
commit | 9b40fbdd31f9366cf4fba4db04e85aa26e7c8a04 (patch) | |
tree | b5f328c7968a70ed1e0041b1ed67ad543fc2f5c3 | |
parent | 36c90356aa291b65661b523e2f79375a72d588b0 (diff) |
lws: sms sending, inbox/outbox view, verbose modem info
-rw-r--r-- | localwebsite/classes/E3372.php | 61 | ||||
-rw-r--r-- | localwebsite/composer.json | 3 | ||||
-rw-r--r-- | localwebsite/composer.lock | 142 | ||||
-rw-r--r-- | localwebsite/config.php | 2 | ||||
-rw-r--r-- | localwebsite/handlers/ModemHandler.php | 128 | ||||
-rw-r--r-- | localwebsite/htdocs/assets/modem.js | 2 | ||||
-rw-r--r-- | localwebsite/htdocs/index.php | 7 | ||||
-rw-r--r-- | localwebsite/templates-web/index.twig | 4 | ||||
-rw-r--r-- | localwebsite/templates-web/modem_data.twig | 20 | ||||
-rw-r--r-- | localwebsite/templates-web/modem_status_page.twig | 3 | ||||
-rw-r--r-- | localwebsite/templates-web/modem_verbose_page.twig | 15 | ||||
-rw-r--r-- | localwebsite/templates-web/sms_page.twig | 40 |
12 files changed, 366 insertions, 61 deletions
diff --git a/localwebsite/classes/E3372.php b/localwebsite/classes/E3372.php index 9f21d02..4c2f27a 100644 --- a/localwebsite/classes/E3372.php +++ b/localwebsite/classes/E3372.php @@ -104,13 +104,6 @@ class E3372 $this->useLegacyTokenAuth = $legacy_token_auth; } - public function __call(string $name, array $arguments) { - if (startsWith($name, 'get')) - $this->auth(); - - return call_user_func_array([$this, $name], $arguments); - } - public function auth() { if ($this->authorized) return; @@ -132,45 +125,65 @@ class E3372 $this->authorized = true; } - - // get* methods have to be protected for __call magic to work - - protected function getDeviceInformation() { + public function getDeviceInformation() { + $this->auth(); return $this->request('device/information'); } - protected function getDeviceSignal() { + public function getDeviceSignal() { + $this->auth(); return $this->request('device/signal'); } - protected function getMonitoringStatus() { + public function getMonitoringStatus() { + $this->auth(); return $this->request('monitoring/status'); } - protected function getNotifications() { + public function getNotifications() { + $this->auth(); return $this->request('monitoring/check-notifications'); } - protected function getDialupConnection() { + public function getDialupConnection() { + $this->auth(); return $this->request('dialup/connection'); } - protected function getTrafficStats() { + public function getTrafficStats() { + $this->auth(); return $this->request('monitoring/traffic-statistics'); } - protected function getSMSCount() { + public function getSMSCount() { + $this->auth(); return $this->request('sms/sms-count'); } - protected function getSMSList($page = 1, $count = 20) { + public function sendSMS(string $phone, string $text) { + $this->auth(); + return $this->request('sms/send-sms', 'POST', [ + 'Index' => -1, + 'Phones' => [ + 'Phone' => $phone + ], + 'Sca' => '', + 'Content' => $text, + 'Length' => -1, + 'Reserved' => 1, + 'Date' => -1 + ]); + } + + public function getSMSList(int $page = 1, int $count = 20, bool $outbox = false) { + $this->auth(); $xml = $this->request('sms/sms-list', 'POST', [ 'PageIndex' => $page, 'ReadCount' => $count, - 'BoxType' => 1, + 'BoxType' => !$outbox ? 1 : 2, 'SortType' => 0, 'Ascending' => 0, - 'UnreadPreferred' => 1 + 'UnreadPreferred' => !$outbox ? 1 : 0 ], true); $xml = simplexml_load_string($xml); @@ -205,8 +218,12 @@ class E3372 curl_setopt($ch, CURLOPT_HTTPHEADER, $this->headers); if ($http_method == 'POST') { curl_setopt($ch, CURLOPT_POST, true); + + $post_data = $this->postDataToXML($data); + // debugLog('post_data:', $post_data); + if (!empty($data)) - curl_setopt($ch, CURLOPT_POSTFIELDS, $this->postDataToXML($data)); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); } $body = curl_exec($ch); @@ -218,7 +235,7 @@ class E3372 return $return_body ? $body : $this->xmlToAssoc($body); } - private function postDataToXML(array $data, int $depth = 1) { + private function postDataToXML(array $data, int $depth = 1): string { if ($depth == 1) return '<?xml version: "1.0" encoding="UTF-8"?>'.$this->postDataToXML(['request' => $data], $depth+1); diff --git a/localwebsite/composer.json b/localwebsite/composer.json index 5cda456..15ba2e5 100644 --- a/localwebsite/composer.json +++ b/localwebsite/composer.json @@ -9,7 +9,8 @@ "ext-curl": "*", "ext-json": "*", "ext-gmp": "*", - "ext-sqlite3": "*" + "ext-sqlite3": "*", + "giggsey/libphonenumber-for-php": "^8.12" }, "license": "MIT" } diff --git a/localwebsite/composer.lock b/localwebsite/composer.lock index e5f3c15..c09f57d 100644 --- a/localwebsite/composer.lock +++ b/localwebsite/composer.lock @@ -4,9 +4,136 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d61e8a038cdf7b7e0b456809da3a1660", + "content-hash": "aad58d1c2f9900517de6f62599845b12", "packages": [ { + "name": "giggsey/libphonenumber-for-php", + "version": "8.12.51", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php.git", + "reference": "a42d89a46797083a95aa48393485fdac22fcac94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/a42d89a46797083a95aa48393485fdac22fcac94", + "reference": "a42d89a46797083a95aa48393485fdac22fcac94", + "shasum": "" + }, + "require": { + "giggsey/locale": "^1.7|^2.0", + "php": ">=5.3.2", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^1.0|^2.0", + "symfony/console": "^2.8|^3.0|^v4.4|^v5.2", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "irc": "irc://irc.appliedirc.com/lobby", + "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php" + }, + "time": "2022-07-11T08:12:34+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.2", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "9c1dca769253f6a3e81f9a5c167f53b6a54ab635" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/9c1dca769253f6a3e81f9a5c167f53b6a54ab635", + "reference": "9c1dca769253f6a3e81f9a5c167f53b6a54ab635", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0", + "symfony/filesystem": "^5.0", + "symfony/finder": "^5.0", + "symfony/process": "^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.2" + }, + "time": "2022-04-06T07:33:59+00:00" + }, + { "name": "symfony/polyfill-ctype", "version": "v1.23.0", "source": { @@ -200,6 +327,15 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [] + "platform": { + "ext-mbstring": "*", + "ext-sockets": "*", + "ext-simplexml": "*", + "ext-curl": "*", + "ext-json": "*", + "ext-gmp": "*", + "ext-sqlite3": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" } diff --git a/localwebsite/config.php b/localwebsite/config.php index d16ef1c..399393c 100644 --- a/localwebsite/config.php +++ b/localwebsite/config.php @@ -52,7 +52,7 @@ return [ 'app.css' => 10, 'app.js' => 4, 'polyfills.js' => 1, - 'modem.js' => 1, + 'modem.js' => 2, 'inverter.js' => 2, ], diff --git a/localwebsite/handlers/ModemHandler.php b/localwebsite/handlers/ModemHandler.php index 88fe463..a874a94 100644 --- a/localwebsite/handlers/ModemHandler.php +++ b/localwebsite/handlers/ModemHandler.php @@ -1,5 +1,9 @@ <?php +use libphonenumber\NumberParseException; +use libphonenumber\PhoneNumberFormat; +use libphonenumber\PhoneNumberUtil; + class ModemHandler extends RequestHandler { @@ -34,11 +38,39 @@ class ModemHandler extends RequestHandler ajax_ok([ 'html' => $this->tpl->render('modem_data.twig', [ 'loading' => false, + 'modem' => $id, 'modem_data' => $modem_data ]) ]); } + public function GET_verbose_page() { + global $config; + + list($modem) = $this->input('modem'); + if (!$modem) + $modem = array_key_first($config['modems']); + + list($signal, $status, $traffic, $device, $dialup_conn) = self::getModemData( + $config['modems'][$modem]['ip'], + $config['modems'][$modem]['legacy_token_auth'], + true); + + $data = [ + ['Signal', $signal], + ['Connection', $status], + ['Traffic', $traffic], + ['Device info', $device], + ['Dialup connection', $dialup_conn] + ]; + $this->tpl->set([ + 'data' => $data, + 'modem_name' => $config['modems'][$modem]['label'], + ]); + $this->tpl->set_title('Подробная информация о модеме '.$modem); + $this->tpl->render_page('modem_verbose_page.twig'); + } + public function GET_routing_smallhome_page() { global $config; @@ -122,41 +154,105 @@ class ModemHandler extends RequestHandler $this->tpl->render_page('routing_dhcp_page.twig'); } - public function GET_sms_page() { + public function GET_sms() { global $config; - list($selected) = $this->input('modem'); + list($selected, $is_outbox, $error, $sent) = $this->input('modem, b:outbox, error, b:sent'); if (!$selected) $selected = array_key_first($config['modems']); $cfg = $config['modems'][$selected]; $e3372 = new E3372($cfg['ip'], $cfg['legacy_token_auth']); - $messages = $e3372->getSMSList(); + $messages = $e3372->getSMSList(1, 20, $is_outbox); $this->tpl->set([ 'modems_list' => array_keys($config['modems']), 'modems' => $config['modems'], 'selected_modem' => $selected, - 'messages' => $messages - ]); - $this->tpl->set_title('Модемы: SMS-сообщения'); + 'messages' => $messages, + 'is_outbox' => $is_outbox, + 'error' => $error, + 'is_sent' => $sent + ]); + + $direction = $is_outbox ? 'исходящие' : 'входящие'; + $this->tpl->set_title('SMS-сообщения ('.$direction.', '.$selected.')'); $this->tpl->render_page('sms_page.twig'); } - protected static function getModemData(string $ip, bool $need_auth = true): array { + public function POST_sms() { + global $config; + + list($selected, $is_outbox, $phone, $text) = $this->input('modem, b:outbox, phone, text'); + if (!$selected) + $selected = array_key_first($config['modems']); + + $return_url = '/sms/?modem='.$selected; + if ($is_outbox) + $return_url .= '&outbox=1'; + + $go_back = function(?string $error = null) use ($return_url) { + if (!is_null($error)) + $return_url .= '&error='.urlencode($error); + else + $return_url .= '&sent=1'; + redirect($return_url); + }; + + $phone = preg_replace('/\s+/', '', $phone); + $country = null; + if (!startsWith($phone, '+')) + $country = 'RU'; + + $phoneUtil = PhoneNumberUtil::getInstance(); + try { + $number = $phoneUtil->parse($phone, $country); + } catch (NumberParseException $e) { + debugError(__METHOD__.': failed to parse number '.$phone.': '.$e->getMessage()); + $go_back('Неверный номер ('.$e->getMessage().')'); + return; + } + + if (!$phoneUtil->isValidNumber($number)) { + $go_back('Неверный номер'); + return; + } + + $phone = $phoneUtil->format($number, PhoneNumberFormat::E164); + + $cfg = $config['modems'][$selected]; + $e3372 = new E3372($cfg['ip'], $cfg['legacy_token_auth']); + + $result = $e3372->sendSMS($phone, $text); + debugLog($result); + + $go_back(); + } + + protected static function getModemData(string $ip, + bool $need_auth = true, + bool $get_raw_data = false): array { $modem = new E3372($ip, $need_auth); + $signal = $modem->getDeviceSignal(); $status = $modem->getMonitoringStatus(); $traffic = $modem->getTrafficStats(); - return [ - 'type' => e3372::getNetworkTypeLabel($status['CurrentNetworkType']), - 'level' => $status['SignalIcon'] ?? 0, - 'rssi' => $signal['rssi'], - 'sinr' => $signal['sinr'], - 'connected_time' => secondsToTime($traffic['CurrentConnectTime']), - 'downloaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentDownload'])), - 'uploaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentUpload'])), - ]; + + if ($get_raw_data) { + $device_info = $modem->getDeviceInformation(); + $dialup_conn = $modem->getDialupConnection(); + return [$signal, $status, $traffic, $device_info, $dialup_conn]; + } else { + return [ + 'type' => e3372::getNetworkTypeLabel($status['CurrentNetworkType']), + 'level' => $status['SignalIcon'] ?? 0, + 'rssi' => $signal['rssi'], + 'sinr' => $signal['sinr'], + 'connected_time' => secondsToTime($traffic['CurrentConnectTime']), + 'downloaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentDownload'])), + 'uploaded' => bytesToUnitsLabel(gmp_init($traffic['CurrentUpload'])), + ]; + } } protected static function getCurrentSmallHomeUpstream() { diff --git a/localwebsite/htdocs/assets/modem.js b/localwebsite/htdocs/assets/modem.js index 2ea4264..9fdb91d 100644 --- a/localwebsite/htdocs/assets/modem.js +++ b/localwebsite/htdocs/assets/modem.js @@ -17,7 +17,7 @@ function ModemStatusUpdater(id) { } extend(ModemStatusUpdater.prototype, { fetch: function() { - ajax.get('/modem/status/get.ajax', { + ajax.get('/modem/get.ajax', { id: this.id }).then(({response}) => { var {html} = response; diff --git a/localwebsite/htdocs/index.php b/localwebsite/htdocs/index.php index 8c9d092..0a4e8c5 100644 --- a/localwebsite/htdocs/index.php +++ b/localwebsite/htdocs/index.php @@ -5,15 +5,16 @@ require_once __DIR__.'/../init.php'; $router = new router; // modem -$router->add('modem/status/', 'Modem status_page'); -$router->add('modem/status/get.ajax', 'Modem status_get_ajax'); +$router->add('modem/', 'Modem status_page'); +$router->add('modem/verbose/', 'Modem verbose_page'); +$router->add('modem/get.ajax', 'Modem status_get_ajax'); $router->add('routing/', 'Modem routing_smallhome_page'); $router->add('routing/switch-small-home/', 'Modem routing_smallhome_switch'); $router->add('routing/{ipsets,dhcp}/', 'Modem routing_${1}_page'); $router->add('routing/ipsets/{add,del}/', 'Modem routing_ipsets_${1}'); -$router->add('modem/sms/', 'Modem sms_page'); +$router->add('sms/', 'Modem sms'); // $router->add('modem/set.ajax', 'Modem ctl_set_ajax'); // inverter diff --git a/localwebsite/templates-web/index.twig b/localwebsite/templates-web/index.twig index 620ad40..4527911 100644 --- a/localwebsite/templates-web/index.twig +++ b/localwebsite/templates-web/index.twig @@ -13,9 +13,9 @@ <h6>Интернет</h6> <ul class="list-group list-group-flush"> - <li class="list-group-item"><a href="/modem/status/">Состояние</a></li> + <li class="list-group-item"><a href="/modem/">Модемы</a></li> <li class="list-group-item"><a href="/routing/">Маршрутизация</a></li> - <li class="list-group-item"><a href="/modem/sms/">SMS-сообщения</a></li> + <li class="list-group-item"><a href="/sms/">SMS-сообщения</a></li> </ul> <h6 class="mt-4">Другое</h6> diff --git a/localwebsite/templates-web/modem_data.twig b/localwebsite/templates-web/modem_data.twig index 55440ea..a2c00e5 100644 --- a/localwebsite/templates-web/modem_data.twig +++ b/localwebsite/templates-web/modem_data.twig @@ -1,12 +1,14 @@ {% if not loading %} -<span class="text-secondary">Сигнал:</span> {% include 'signal_level.twig' with {'level': modem_data.level} %}<br> -<span class="text-secondary">Тип сети:</span> <b>{{ modem_data.type }}</b><br> -<span class="text-secondary">RSSI:</span> {{ modem_data.rssi }}<br/> -{% if modem_data.sinr %} - <span class="text-secondary">SINR:</span> {{ modem_data.sinr }}<br/> -{% endif %} -<span class="text-secondary">Время соединения:</span> {{ modem_data.connected_time }}<br> -<span class="text-secondary">Принято/передано:</span> {{ modem_data.downloaded }} / {{ modem_data.uploaded }} + <span class="text-secondary">Сигнал:</span> {% include 'signal_level.twig' with {'level': modem_data.level} %}<br> + <span class="text-secondary">Тип сети:</span> <b>{{ modem_data.type }}</b><br> + <span class="text-secondary">RSSI:</span> {{ modem_data.rssi }}<br/> + {% if modem_data.sinr %} + <span class="text-secondary">SINR:</span> {{ modem_data.sinr }}<br/> + {% endif %} + <span class="text-secondary">Время соединения:</span> {{ modem_data.connected_time }}<br> + <span class="text-secondary">Принято/передано:</span> {{ modem_data.downloaded }} / {{ modem_data.uploaded }} + <br> + <a href="/modem/verbose/?modem={{ modem }}">Подробная информация</a> {% else %} -{% include 'spinner.twig' %} + {% include 'spinner.twig' %} {% endif %}
\ No newline at end of file diff --git a/localwebsite/templates-web/modem_status_page.twig b/localwebsite/templates-web/modem_status_page.twig index f2b999b..3f20b86 100644 --- a/localwebsite/templates-web/modem_status_page.twig +++ b/localwebsite/templates-web/modem_status_page.twig @@ -8,7 +8,8 @@ <h6 class="text-primary{% if not loop.first %} mt-4{% endif %}">{{ modem.label }}</h6> <div id="modem_data_{{ modem_key }}"> {% include 'modem_data.twig' with { - loading: true + loading: true, + modem: modem_key } %} </div> {% endfor %} diff --git a/localwebsite/templates-web/modem_verbose_page.twig b/localwebsite/templates-web/modem_verbose_page.twig new file mode 100644 index 0000000..3b4c25e --- /dev/null +++ b/localwebsite/templates-web/modem_verbose_page.twig @@ -0,0 +1,15 @@ +{% include 'bc.twig' with { + history: [ + {link: '/modem/', text: "Модемы" }, + {text: modem_name} + ] +} %} + +{% for item in data %} + {% set item_name = item[0] %} + {% set item_data = item[1] %} + <h6 class="text-primary mt-4">{{ item_name }}</h6> + {% for k, v in item_data %} + {{ k }} = {{ v }}<br> + {% endfor %} +{% endfor %}
\ No newline at end of file diff --git a/localwebsite/templates-web/sms_page.twig b/localwebsite/templates-web/sms_page.twig index f60d223..112fa64 100644 --- a/localwebsite/templates-web/sms_page.twig +++ b/localwebsite/templates-web/sms_page.twig @@ -7,14 +7,50 @@ <nav> <div class="nav nav-tabs" id="nav-tab"> {% for modem in modems_list %} - {% if selected_modem != modem %}<a href="/modem/sms/?modem={{ modem }}" class="text-decoration-none">{% endif %} + {% if selected_modem != modem %}<a href="/sms/?modem={{ modem }}" class="text-decoration-none">{% endif %} <button class="nav-link{% if modem == selected_modem %} active{% endif %}" type="button">{{ modems[modem].short_label }}</button> {% if selected_modem != modem %}</a>{% endif %} {% endfor %} </div> </nav> -<h6 class="text-primary mt-4">Последние входящие</h6> +<h6 class="text-primary mt-4">Отправить SMS</h6> + +{% if is_sent %} + <div class="alert alert-success" role="alert"> + Сообщение отправлено. + </div> +{% elseif error %} + <div class="alert alert-danger" role="alert"> + {{ error }} + </div> +{% endif %} + +<div> + <form method="post" action="/sms/"> + <input type="hidden" name="modem" value="{{ selected_modem }}"> + <div class="form-floating mb-3"> + <input type="text" name="phone" class="form-control" id="inputPhone" placeholder="+7911xxxyyzz"> + <label for="inputPhone">Телефон</label> + </div> + <div class="form-floating"> + <textarea class="form-control" id="inputTA" name="text" placeholder="Hello world" style="height: 100px"></textarea> + <label for="inputTA">Текст сообщения</label> + </div> + <div class="mt-3"> + <button type="submit" class="btn btn-primary">Отправить</button> + </div> + </form> +</div> + +<h6 class="text-primary mt-4"> + Последние + {% if not is_outbox %} + <b>входящие</b> <span class="text-black-50">|</span> <a href="/sms/?modem={{ selected_modem }}&outbox=1">исходящие</a> + {% else %} + <a href="/sms/?modem={{ selected_modem }}">входящие</a> <span class="text-black-50">|</span> <b>исходящие</b> + {% endif %} +</h6> {% for m in messages %} <div class="mt-3"> |