diff --git a/devices/button/button_esp8266/button_esp8266.ino b/devices/button/button_esp8266/button_esp8266.ino index 932c9a0..32aa9e6 100755 --- a/devices/button/button_esp8266/button_esp8266.ino +++ b/devices/button/button_esp8266/button_esp8266.ino @@ -4,7 +4,7 @@ #define BUTTON_CHANNEL_NUM 4 const char* DEVICE_TYPE = "button"; -const char* FW_VERSION = "1.10 dev"; +const char* FW_VERSION = "1.12 dev"; const uint8_t CHANNEL_NUM = BUTTON_CHANNEL_NUM; #include diff --git a/docs/server-api-v1/scripts.md b/docs/server-api-v1/scripts.md index 0f6ba0b..3ee95b5 100644 --- a/docs/server-api-v1/scripts.md +++ b/docs/server-api-v1/scripts.md @@ -28,6 +28,7 @@ { "alias": "script_alias", "name": "script name", + "state": "enabled", "description": "script description", "filename": "TestScriptsScope.php", "path": "/srv/http/smart-home-serv.local/server/ControlScripts", @@ -53,6 +54,7 @@ { "name": "TestScriptsScope", "filename": "TestScriptsScope.php", + "state": "enabled", "path": "/srv/http/smart-home-serv.local/server/ControlScripts" } ], @@ -73,9 +75,13 @@ "data": { "scopes": [ { - "name": "TestScriptsScope", + "alias": "script_alias", + "name": "script name", + "state": "enabled", + "description": "script description", "filename": "TestScriptsScope.php", - "path": "/srv/http/smart-home-serv.local/server/ControlScripts" + "path": "/srv/http/smart-home-serv.local/server/ControlScripts", + "created_by": "Eugene Sukhodolskiy" } ], "total": 1 @@ -118,13 +124,13 @@ --- -### POST `/api/v1/scripts/scopes/name/{{name}}/update` +### POST `/api/v1/scripts/scopes/update` Редактировать scope скриптов #### Пример запроса ```json { - "alias": "bad_weather_light", + "name": "bad_weather_light", "filename": "bad_weather_light.php", "path": "/home/smartserver/scripts", } @@ -134,8 +140,8 @@ ```json { "status": false, - "field": "alias", - "message": "Alias already exists" // | "File not exists" | "Script by id not found" + "field": "name", + "message": "File not exists" // | "Scope name not found" } ``` diff --git a/server/ControlScripts/SpotlightsScope.php b/server/ControlScripts/SpotlightsScope.php index 0c60278..5eed969 100644 --- a/server/ControlScripts/SpotlightsScope.php +++ b/server/ControlScripts/SpotlightsScope.php @@ -11,6 +11,30 @@ 2 => "spotlight_main_front_1" ]; + protected $sync_map = [ + "connections" => [ + [ + ["type" => "relay", "alias" => "spotlight_main_back_1", "channel" => 0], + ["type" => "button", "alias" => "buttons_backdoor", "channel" => 0], + ], + + [ + ["type" => "relay", "alias" => "spotlight_main_back_2", "channel" => 0], + ["type" => "button", "alias" => "buttons_backdoor", "channel" => 1], + ], + + [ + ["type" => "relay", "alias" => "spotlight_main_front_1", "channel" => 0], + ["type" => "button", "alias" => "buttons_backdoor", "channel" => 2], + ], + ] + ]; + + /** + * Example + * link_state_mapping("relay_alias:0", "button_alias:1"); + */ + public function register_events_handlers(): void { $this -> backdoor_btns_online(); $this -> backdoor_btns_handlers(); @@ -70,7 +94,7 @@ if(isset($status["channels"][0]["state"]) and $status["channels"][0]["state"] != ($state ? "on" : "off")) { $result = $relay_api -> set_state($state); - $this -> sync_relay_channel_btn_channel($relay_api, $btns_block_api, 0, $btn_channel); + $this -> helper() -> sync_relay_to_btn_channel($relay_api, $btns_block_api, 0, $btn_channel); } } @@ -83,36 +107,24 @@ } // EVENTS HANDLERS - - /** - * Helper. - * Нужен для синхронизации состояния реле и канала блока кнопок. - * TODO: Это нужно оформить как официальный хелпер - * @param Relay $relay - * @param Button $btn - * @param int|integer $relay_channel - * @param int|integer $btn_channel - * @return void - */ - public function sync_relay_channel_btn_channel(\SHServ\Tools\DeviceAPI\Relay $relay_api, \SHServ\Tools\DeviceAPI\Button $btn_block_api, int $relay_channel = 0, int $btn_channel = 0): void { - $relay_channels = ($relay_api -> get_status())["channels"]; - $btn_block_api -> set_channel_state( - $relay_channels[$relay_channel]["state"] == "on" ? "enabled" : "disabled", - $btn_channel - ); - } - - protected function backdoor_btns_online() { + protected function backdoor_btns_online(): void { $this -> add_event_handler("button@buttons_backdoor.online", function(Device $btns_block, Array $data) { $btns_block_api = $btns_block -> device_api(); if($btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { $btns_block_api -> set_channel_state("mute", 3); + + foreach($this -> aliases as $btn_channel => $relay_alias) { + $relay_api = $this -> devices() -> by_alias($relay_alias) -> device_api(); + if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay) { + $this -> helper() -> sync_relay_to_btn_channel($relay_api, $btns_block_api, 0, $btn_channel); + } + } } }); } - protected function backdoor_btns_handlers() { + protected function backdoor_btns_handlers(): void { foreach($this -> aliases as $btn_channel => $relay_alias) { $this -> add_event_handler("button@buttons_backdoor({$btn_channel}).press", function(Device $btns_block, Array $data) use ($btn_channel, $relay_alias) { $btns_block_api = $btns_block -> device_api(); @@ -120,7 +132,7 @@ if($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay and $btns_block_api instanceof \SHServ\Tools\DeviceAPI\Button) { $relay_api -> toggle_channel(0); - $this -> sync_relay_channel_btn_channel($relay_api, $btns_block_api, 0, $btn_channel); + $this -> helper() -> sync_relay_to_btn_channel($relay_api, $btns_block_api, 0, $btn_channel); } }); } diff --git a/server/SHServ/App.php b/server/SHServ/App.php index 406cb87..ddaf4a3 100644 --- a/server/SHServ/App.php +++ b/server/SHServ/App.php @@ -25,7 +25,7 @@ public $control_scripts_instances = []; protected $required_control_scripts_instance; - public function __construct(){ + public function __construct() { parent::__construct(); global $argv; @@ -33,20 +33,20 @@ $this -> app_init(); } - public function app_init(){ + public function app_init(): void { if(!$this -> console_flag) { $this -> error_handlers = new ErrorHandler(); } \Fury\Modules\Template\Template::set_driver(new \Fury\Drivers\TemplateDriver()); - $this -> control_scripts_init(); $this -> router_json_to_post_emulate(); $this -> devtools = new DevTools(); $this -> router = new Router(); $this -> routes = new Routes($this -> router); $this -> thin_builder = new ThinBuilder(FCONF['db'], new \Fury\Drivers\ThinBuilderDriver(bootstrap())); + $this -> control_scripts_init(); $this -> events_handlers = new EventsHandlers(); $this -> events_handlers -> handlers(); @@ -56,12 +56,12 @@ $this -> factory = new Factory(); } - public function root_folder(){ + public function root_folder(): String { list($root) = explode('SHServ', __DIR__); return $root; } - protected function router_json_to_post_emulate() { + protected function router_json_to_post_emulate(): void { $content_type = $_SERVER['CONTENT_TYPE'] ?? ''; if (stripos($content_type, 'application/json') === false) { @@ -80,7 +80,7 @@ } } - public function control_scripts_init() { + public function control_scripts_init(): void { $this -> required_control_scripts_instance = new \SHServ\RequiredControlScriptsScope(); $scripts_dir = scandir(__DIR__ . "/../ControlScripts/"); diff --git a/server/SHServ/Controllers/CronController.php b/server/SHServ/Controllers/CronController.php index 521908b..ebd9061 100644 --- a/server/SHServ/Controllers/CronController.php +++ b/server/SHServ/Controllers/CronController.php @@ -5,14 +5,18 @@ use \SHServ\Models\Devices; use \SHServ\Tools\DeviceScanner; use \SHServ\Middleware\ControlScripts; +use \SHServ\Models\Scripts; class CronController extends \SHServ\Middleware\Controller { public function run_regular_cron_scripts() { - // В будущем: фильтруем список, отсеивая те скрипты, что выключены - + $scripts_model = new Scripts(); $regular_scripts = ControlScripts::get_regular_scripts(); foreach($regular_scripts as $alias => $script) { + if(!$scripts_model -> script_state("regular", $alias)) { + continue; + } + $script["script"](); } } diff --git a/server/SHServ/Controllers/ScriptsRESTAPIController.php b/server/SHServ/Controllers/ScriptsRESTAPIController.php index a7997ff..8e41647 100644 --- a/server/SHServ/Controllers/ScriptsRESTAPIController.php +++ b/server/SHServ/Controllers/ScriptsRESTAPIController.php @@ -1,5 +1,13 @@ $script_data) { - $data[] = [ - "alias" => $alias, - "type" => "action", - "name" => $script_data["attributes"]["name"], - "description" => $script_data["attributes"]["description"], - "filename" => $script_data["attributes"]["filename"], - "path" => $script_data["attributes"]["path"], - "created_by" => $script_data["attributes"]["author"], - ]; - } + $data = (new Scripts()) -> actions_scripts_list(); return $this -> utils() -> response_success([ "scripts" => $data, @@ -42,21 +36,7 @@ } public function regular_scripts_list() { - $scripts = ControlScripts::get_regular_scripts(); - - $data = []; - - foreach($scripts as $alias => $script_data) { - $data[] = [ - "alias" => $alias, - "type" => "regular", - "name" => $script_data["attributes"]["name"], - "description" => $script_data["attributes"]["description"], - "filename" => $script_data["attributes"]["filename"], - "path" => $script_data["attributes"]["path"], - "created_by" => $script_data["attributes"]["author"], - ]; - } + $data = (new Scripts()) -> regular_scripts_list(); return $this -> utils() -> response_success([ "scripts" => $data, @@ -96,8 +76,8 @@ return $file; } - public function scope_update($name, $file) { - + public function scope_update($name, $path, $file) { + } public function scope_remove($name) { diff --git a/server/SHServ/Helpers/DeviceScriptsHelper.php b/server/SHServ/Helpers/DeviceScriptsHelper.php new file mode 100644 index 0000000..62a41de --- /dev/null +++ b/server/SHServ/Helpers/DeviceScriptsHelper.php @@ -0,0 +1,120 @@ + devices_model = $devices_model; + } + + public function devices(): Devices { + return $this -> devices_model; + } + + + /** + * Синхронизировать указанный канал реле с указанным каналом устройства кнопки. + * Синхронизация низкоуровневая и одностороняя, от реле к кнопке. + * @param Relay $relay + * @param Button $btn + * @param int|integer $relay_channel + * @param int|integer $btn_channel + * @return void + */ + public function sync_relay_to_btn_channel(\SHServ\Tools\DeviceAPI\Relay $relay_api, \SHServ\Tools\DeviceAPI\Button $btn_block_api, int $relay_channel = 0, int $btn_channel = 0): void { + $relay_channels = ($relay_api -> get_status())["channels"]; + + $btn_block_api -> set_channel_state( + $relay_channels[$relay_channel]["state"] == "on" ? "enabled" : "disabled", + $btn_channel + ); + } + + /** + * Синхронизировать состояние каналов реле с соответствующими блоками кнопок с указанными каналами. + * Синхронизация односторонняя от реле к кнопкам + * @param Array $sync_map [ + * relay_channel_num => [ + * ["alias" => "button_alias", "channel" => 1], + * ["alias" => "button_alias_2", "channel" => 0], + * ] + * ] + * @param String $relay_alias + * @return Bool + */ + public function sync_relay_to_btns(Array $sync_map, String $relay_alias): Bool { + $relay = $this -> devices() -> by_alias($relay_alias); + if(!$relay) { + return false; + } + + $relay_api = $relay -> device_api(); + if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) { + return false; + } + + foreach($sync_map as $relay_channel => $btns) { + foreach($btns as $item) { + $btn = $this -> devices() -> by_alias($item["alias"]); + if(!$btn) { + continue; + } + + $btn_api = $btn -> device_api(); + if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) { + continue; + } + + $this -> sync_relay_to_btn_channel($relay_api, $btn_api, $relay_channel, intval($item["channel"])); + } + } + + return true; + } + + /** + * Синхронизировать состояние каналов устройства "кнопка" с соответствующими реле с указанными каналами. + * Синхронизация односторонняя от реле к кнопкам + * @param Array $sync_map [ + * button_channel_num => [ + * ["alias" => "relay_alias", "channel" => 1], + * ["alias" => "relay_alias_2", "channel" => 0], + * ] + * ] + * @param String $btn_alias + * @return Bool + */ + public function sync_btn_channels(Array $sync_map, String $btn_alias): Bool { + $btn = $this -> devices() -> by_alias($btn_alias); + if(!$btn) { + return false; + } + + $btn_api = $btn -> device_api(); + if(!($btn_api instanceof \SHServ\Tools\DeviceAPI\Button)) { + return false; + } + + foreach($sync_map as $btn_channel => $relays) { + foreach($relays as $item) { + $relay = $this -> devices() -> by_alias($item["alias"]); + if(!$relay) { + continue; + } + + $relay_api = $relay -> device_api(); + if(!($relay_api instanceof \SHServ\Tools\DeviceAPI\Relay)) { + continue; + } + + $this -> sync_relay_to_btn_channel($relay_api, $btn_api, intval($item["channel"]), $btn_channel); + } + } + + return true; + } +} \ No newline at end of file diff --git a/server/SHServ/Middleware/ControlScripts.php b/server/SHServ/Middleware/ControlScripts.php index b8bfcf8..edda39e 100644 --- a/server/SHServ/Middleware/ControlScripts.php +++ b/server/SHServ/Middleware/ControlScripts.php @@ -3,9 +3,12 @@ namespace SHServ\Middleware; use \SHServ\Models\Devices; +use \SHServ\Models\Scripts; +use \SHServ\Helpers\DeviceScriptsHelper; abstract class ControlScripts { protected $devices_model; + protected $device_scripts_helper; protected static $regular_scripts = []; protected static $actions_scripts = []; @@ -14,9 +17,13 @@ abstract protected function register_actions_scripts(): void; public function __construct() { - $this -> register_events_handlers(); - $this -> register_regular_scripts(); - $this -> register_actions_scripts(); + list($scope_folder, $scope_name) = explode("\\", str_replace("SHServ", "", static::class)); + + if($scope_folder != "ControlScripts" or (new Scripts()) -> script_state("scope", $scope_name)) { + $this -> register_events_handlers(); + $this -> register_regular_scripts(); + $this -> register_actions_scripts(); + } } protected function add_event_handler(String $event_name, callable $handler): void { @@ -33,6 +40,14 @@ return $this -> devices_model; } + protected function helper(): DeviceScriptsHelper { + if(!$this -> device_scripts_helper) { + $this -> device_scripts_helper = new DeviceScriptsHelper($this -> devices()); + } + + return $this -> device_scripts_helper; + } + protected function add_regular_script(Array $attributes, callable $script): bool { if(!isset($attributes["alias"])) { return false; @@ -100,6 +115,11 @@ return null; } + $scripts_model = new Scripts(); + if(!$scripts_model -> script_state("action", $alias)) { + return null; + } + $start_time = microtime(true); $result = self::$actions_scripts[$alias]["script"]($params); diff --git a/server/SHServ/Models/Scripts.php b/server/SHServ/Models/Scripts.php index d5e6a55..59a9dd7 100644 --- a/server/SHServ/Models/Scripts.php +++ b/server/SHServ/Models/Scripts.php @@ -2,9 +2,11 @@ namespace SHServ\Models; +use \SHServ\Middleware\ControlScripts; use \SHServ\Entities\Script; class Scripts extends \SHServ\Middleware\Model { + public function get_scopes_list(): Array { $instances = app() -> control_scripts_instances; $scopes = []; @@ -16,7 +18,8 @@ $scopes[] = [ "name" => $name, "filename" => $path_info["basename"], - "path" => $path_info["dirname"] + "path" => $path_info["dirname"], + "state" => $this -> script_state("scope", $name) ? "enabled" : "disabled" ]; } @@ -58,11 +61,11 @@ } public function set_script_state(String $type, String $uniq_name, Bool $state): Bool { - $result = $this -> thin_builder() -> select([ + $result = $this -> thin_builder() -> select( Script::$table_name, Script::get_fields(), [ ["type", "=", $type], "AND", [ "uniq_name", '=', $uniq_name ] ] - ]); + ); if(!$result) { return $this -> thin_builder() -> insert( @@ -73,13 +76,13 @@ "state" => $state ? "enabled" : "disabled", "create_at" => date("Y-m-d H:i:s") ] - ); + ) ? true : false; } // update $script = new Script($result[0]["id"], $result[0]); $script -> state = $state ? "enabled" : "disabled"; - return $script -> update(); + return $script -> update() ? true : false; } public function enable_script(String $type, String $uniq_name): Bool { @@ -89,4 +92,31 @@ public function disable_script(String $type, String $uniq_name): Bool { return $this -> set_script_state($type, $uniq_name, false); } + + public function regular_scripts_list(): Array { + return $this -> get_scripts_list( "regular", ControlScripts::get_regular_scripts() ); + } + + public function actions_scripts_list(): Array { + return $this -> get_scripts_list( "action", ControlScripts::get_actions_scripts() ); + } + + public function get_scripts_list(String $type, Array $scripts): Array { + $data = []; + + foreach($scripts as $alias => $script_data) { + $data[] = [ + "alias" => $alias, + "type" => $type, + "name" => $script_data["attributes"]["name"], + "state" => $this -> script_state($type, $alias) ? "enabled" : "disabled", + "description" => $script_data["attributes"]["description"], + "filename" => $script_data["attributes"]["filename"], + "path" => $script_data["attributes"]["path"], + "created_by" => $script_data["attributes"]["author"], + ]; + } + + return $data; + } } \ No newline at end of file diff --git a/server/SHServ/Routes.php b/server/SHServ/Routes.php index b0bc515..d447ebb 100644 --- a/server/SHServ/Routes.php +++ b/server/SHServ/Routes.php @@ -50,11 +50,6 @@ } } - // Examples - // $this -> router -> uri("/", "{$this -> cn}\\SearchController@search_page"); - // $this -> router -> uri('/not-found.html', "{$this -> cn}\\InfoPagesController@not_found_page"); - - // $this -> router -> uri('/uadpost/$alias', "{$this -> cn}\\UAdPostController@view_page"); protected function uri_routes() { $this -> router -> uri("/", function(){ return "Smart home server.
Version 0.1 dev"; @@ -62,16 +57,10 @@ $this -> router -> uri("/cron/regular-scripts", "{$this -> cn}\\CronController@run_regular_cron_scripts"); $this -> router -> uri("/cron/status-update-scanning", "{$this -> cn}\\CronController@status_update_scanning"); - - } protected function get_routes() { - // $this -> router -> get( - // [ "uadpost_id", "state" ], - // "{$this -> cn}\\UAdPostController@change_uadpost_state", - // "/profile/uadposts/change-state.html" - // ); + } protected function post_routes() { diff --git a/server/SHServ/Routes/ScriptsRESTAPI_v1.php b/server/SHServ/Routes/ScriptsRESTAPI_v1.php index be7326f..edb7176 100644 --- a/server/SHServ/Routes/ScriptsRESTAPI_v1.php +++ b/server/SHServ/Routes/ScriptsRESTAPI_v1.php @@ -11,8 +11,8 @@ $this -> router -> uri('/api/v1/scripts/actions/alias/$uniq_name/enable', "{$this -> cn}\\ScriptsRESTAPIController@action_script_enable"); $this -> router -> uri('/api/v1/scripts/actions/alias/$uniq_name/disable', "{$this -> cn}\\ScriptsRESTAPIController@action_script_disable"); - $this -> router -> uri('/api/v1/scripts/scopes/name/$uniq_name/enable', "{$this -> cn}\\ScriptsRESTAPIController@scopes_enable"); - $this -> router -> uri('/api/v1/scripts/scopes/name/$uniq_name/disable', "{$this -> cn}\\ScriptsRESTAPIController@scopes_disable"); + $this -> router -> uri('/api/v1/scripts/scopes/name/$uniq_name/enable', "{$this -> cn}\\ScriptsRESTAPIController@scope_enable"); + $this -> router -> uri('/api/v1/scripts/scopes/name/$uniq_name/disable', "{$this -> cn}\\ScriptsRESTAPIController@scope_disable"); $this -> router -> uri('/api/v1/scripts/regular/alias/$uniq_name/enable', "{$this -> cn}\\ScriptsRESTAPIController@regular_script_enable"); $this -> router -> uri('/api/v1/scripts/regular/alias/$uniq_name/disable', "{$this -> cn}\\ScriptsRESTAPIController@regular_script_disable"); } @@ -24,6 +24,12 @@ '/api/v1/scripts/actions/run' ); + $this -> router -> post( + [ "name", "path", "file" ], + "{$this -> cn}\\ScriptsRESTAPIController@scope_update", + '/api/v1/scripts/actions/run' + ); + } protected function scripts_restapi_get_routes() {