diff --git a/jsk_aibo_robot/aibo_selenium_ros/.gitignore b/jsk_aibo_robot/aibo_selenium_ros/.gitignore new file mode 100644 index 0000000000..c89f6bc8b5 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/.gitignore @@ -0,0 +1,52 @@ +devel/ +logs/ +build/ +bin/ +lib/ +msg_gen/ +srv_gen/ +msg/*Action.msg +msg/*ActionFeedback.msg +msg/*ActionGoal.msg +msg/*ActionResult.msg +msg/*Feedback.msg +msg/*Goal.msg +msg/*Result.msg +msg/_*.py +build_isolated/ +devel_isolated/ + +# Generated by dynamic reconfigure +*.cfgc +/cfg/cpp/ +/cfg/*.py + +# Ignore generated docs +*.dox +*.wikidoc + +# eclipse stuff +.project +.cproject + +# qcreator stuff +CMakeLists.txt.user + +srv/_*.py +*.pcd +*.pyc +qtcreator-* +*.user + +/planning/cfg +/planning/docs +/planning/src + +*~ + +# Emacs +.#* + +# Catkin custom files +CATKIN_IGNORE +.vscode/* diff --git a/jsk_aibo_robot/aibo_selenium_ros/CMakeLists.txt b/jsk_aibo_robot/aibo_selenium_ros/CMakeLists.txt new file mode 100644 index 0000000000..f2f4f58491 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8.3) +project(aibo_selenium_ros) + +find_package(catkin REQUIRED catkin_virtualenv) + +catkin_python_setup() + +catkin_generate_virtualenv( + PYTHON_INTERPRETER python3 + CHECK_VENV FALSE +) + +catkin_package() + +file(GLOB NODE_SCRIPTS_FILES node_scripts/*.py) + +catkin_install_python( + PROGRAMS ${NODE_SCRIPTS_FILES} + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) diff --git a/jsk_aibo_robot/aibo_selenium_ros/README.md b/jsk_aibo_robot/aibo_selenium_ros/README.md new file mode 100644 index 0000000000..81065118c8 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/README.md @@ -0,0 +1,65 @@ +# aibo_selenium_ros + +This package provides a ROS Node which retieve head camera images from web browser and publish it as ROS message. + +## How to setup + +**Currently this node is only tested with ROS noetic.** + +First, Download [suitable version of chrome webdriver to Google chrome installed](https://chromedriver.chromium.org/downloads) and place it somewhere. + +And install catkin dependencies + +```bash +cd +rosdep install --from-paths . --ignore-src -y -r +``` + +Build + +```bash +catkin bt +``` + +## How to run (Manual mode) + +To run this node, first run roscore + +```bash +roscore +``` + +Then run `main.py` + +```bash +$ rosrun aibo_selenium_ros main.py + +Press Enter when logging if completed. +``` + +This script shows up a selenium controlled webbrowser window. + +![aibo_login](https://user-images.githubusercontent.com/9410362/237017652-29c64750-a3a6-4008-a197-9c2cc5ba5bdb.png) + +Please sign in to myaibo manually and open the dashboard page below. + +![dashboard](https://user-images.githubusercontent.com/9410362/237017806-9f85e696-1f1c-4362-b1cc-22b0fe0e7635.png) + +Then hit Enter key on the `main.py` window. So it will start to control browser and publish image. + +![output](https://user-images.githubusercontent.com/9410362/237018221-13d6a118-6ec2-44ff-86eb-eecf75cfe218.gif) + + +## How to run (Auto-login mode) + +You may be able to use `auto_login` mode. **This mode is unstable** + +```bash +rosrun aibo_selenium_ros main.py _login_id:= _login_password:= _auto_login:=true +``` + +You can also use headless mode. by setting parameter. + +```bash +rosrun aibo_selenium_ros main.py _login_id:= _login_password:= _auto_login:=true _headless:=true +``` diff --git a/jsk_aibo_robot/aibo_selenium_ros/node_scripts/main.py b/jsk_aibo_robot/aibo_selenium_ros/node_scripts/main.py new file mode 100644 index 0000000000..72aacdc3ce --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/node_scripts/main.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import logging +import sys +import time + +import cv2 +import rospy +from cv_bridge import CvBridge +from sensor_msgs.msg import CompressedImage, Image + +from aibo_selenium_ros import AIBOBrowserInterface + +if __name__ == '__main__': + + rospy.init_node('aibo_selenium_ros') + + pub_raw = rospy.Publisher('~image', Image, queue_size=1) + pub_compressed = rospy.Publisher('~image/compressed', + CompressedImage, + queue_size=1) + cv_bridge = CvBridge() + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + login_id = rospy.get_param('~login_id', '') + login_password = rospy.get_param('~login_password', '') + + auto_login = bool(rospy.get_param('~auto_login', False)) + headless = bool(rospy.get_param('~headless', False)) + + interface = AIBOBrowserInterface(login_id=login_id, + login_pw=login_password, + auto_login=auto_login, + headless=headless) + if not auto_login: + input('Press Enter when logging if completed.') + interface.initialized = True + + if not interface.initialized: + rospy.logerr('Initialization failed.') + input('Press Enter when logging if completed.') + + interface.start_watching() + while True: + image_rgb = interface.get_current_image() + if image_rgb is None: + rospy.loginfo('Restarting watching....') + time.sleep(3.) + ret = interface.continue_watching() + rospy.loginfo('Restarted watching.') + else: + if pub_raw.get_num_connections() > 0: + msg_raw = cv_bridge.cv2_to_imgmsg(cv2.cvtColor(image_rgb, + cv2.COLOR_RGBA2RGB), + encoding='rgb8') + pub_raw.publish(msg_raw) + rospy.loginfo('Publish a raw message') + + if pub_compressed.get_num_connections() > 0: + msg_compressed = cv_bridge.cv2_to_compressed_imgmsg(cv2.cvtColor( + image_rgb, cv2.COLOR_RGBA2RGB), + dst_format='jpg') + pub_compressed.publish(msg_compressed) + rospy.loginfo('Publish a compressed message') diff --git a/jsk_aibo_robot/aibo_selenium_ros/node_scripts/test_interface.py b/jsk_aibo_robot/aibo_selenium_ros/node_scripts/test_interface.py new file mode 100644 index 0000000000..686d81acbc --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/node_scripts/test_interface.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +import argparse +import logging +import sys +import time + +import cv2 + +from aibo_selenium_ros import AIBOBrowserInterface + +if __name__ == '__main__': + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + parser = argparse.ArgumentParser() + + parser.add_argument('--login-id', default='') + parser.add_argument('--login-password', default='') + + parser.add_argument('--auto-login', action='store_true') + parser.add_argument('--headless', action='store_true') + + args = parser.parse_args() + + interface = AIBOBrowserInterface(login_id=args.login_id, + login_pw=args.login_password, + auto_login=args.auto_login, + headless=args.headless) + if not args.auto_login: + input('Press Enter when logging if completed.') + interface.initialized = True + + if not interface.initialized: + logger.error('Initialization failed.') + sys.exit(1) + + interface.start_watching() + while True: + image_rgb = interface.get_current_image() + if image_rgb is None: + logger.info('Restarting watching....') + time.sleep(3.) + ret = interface.continue_watching() + logger.info('Restarted watching.') + else: + image_bgr = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2BGR) + cv2.imshow('hoge', image_bgr) + if cv2.waitKey(1) != -1: + break + + cv2.destroyAllWindows() diff --git a/jsk_aibo_robot/aibo_selenium_ros/package.xml b/jsk_aibo_robot/aibo_selenium_ros/package.xml new file mode 100644 index 0000000000..d592b071f3 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/package.xml @@ -0,0 +1,22 @@ + + + aibo_selenium_ros + 1.1.0 + The aibo_selenium_ros package + + Koki Shinjo + Kei Okada + + Koki Shinjo + + BSD + + catkin + catkin_virtualenv + + chromium-browser + chromedriver + + requirements.txt + + diff --git a/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/__init__.py b/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/__init__.py new file mode 100644 index 0000000000..ecad758e09 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/__init__.py @@ -0,0 +1,179 @@ +import base64 +import io +import logging +import random +import time + +import selenium +import undetected_chromedriver as uc +from imageio import imread +from selenium.webdriver.chrome.options import Options +from selenium.webdriver.common.by import By + +logger = logging.getLogger(__name__) + + +class AIBOBrowserInterface(object): + + def __init__(self, + webdriver_path='/usr/bin/chromedriver', + chrome_executable_path='/usr/bin/chromium-browser', + login_page_url='https://myaibo.aibo.sony.jp/#/', + login_id='', + login_pw='', + auto_login=True, + headless=False, + timeout=20.): + + self.initialized = False + + options = Options() + if headless: + options.add_argument('--headless') + if chrome_executable_path is not None: + options.binary_location = chrome_executable_path + + self.driver = uc.Chrome(executable_path=webdriver_path, options=options, version_main=115) + logger.info(f"navigator.webdriver: {self.driver.execute_script('return navigator.webdriver')}") + + if not auto_login: + self.driver.get(login_page_url) + logger.info('Opened default page') + return + + # Open login page + self.driver.get(login_page_url) + logger.info('Opened default page') + + # Press いますぐサインイン + self.driver.find_elements(By.TAG_NAME, value='button')[0].click() + logger.info('Opened login page') + + # Input login ID and enter + deadline = time.perf_counter() + timeout + success = False + while time.perf_counter() < deadline: + try: + e_input = self.driver.find_element(By.ID, value='input-sign-in-id') + e_button = self.driver.find_element(By.ID, value='button-sign-in') + success = True + break + except selenium.common.exceptions.NoSuchElementException: + continue + if not success: + logger.error('Failed to continue login process. Please proceed manually.') + return + else: + time.sleep(1.) + e_input.send_keys(login_id) + e_button.click() + logger.info('Input ID and clicked button') + + # Input PW and login + deadline = time.perf_counter() + timeout + success = False + while time.perf_counter() < deadline: + try: + e_input = self.driver.find_element( + By.ID, value='signin-password-input-password') + e_button = self.driver.find_element(By.ID, + value='signin-password-button') + success = True + break + except selenium.common.exceptions.NoSuchElementException: + continue + if not success: + logger.error('Failed to continue login process. Please proceed manually.') + return + else: + time.sleep(1.) + e_input.send_keys(login_pw) + e_button.click() + logger.info('Input PW and clicked button') + + time.sleep(10.) + deadline = time.perf_counter() + timeout + success = False + while time.perf_counter() < deadline: + try: + for b in self.driver.find_elements(By.TAG_NAME, value='button'): + if b.accessible_name == '今なに見てる?': + success = True + break + except selenium.common.exceptions.NoSuchElementException: + continue + if success: + logger.info('Initialized') + self.initialized = True + else: + logger.error('Failed to continue login process. Please proceed manually.') + + def start_watching(self, timeout=5.): + + try: + for b in self.driver.find_elements(By.TAG_NAME, value='button'): + if b.accessible_name == '今なに見てる?' and b.is_displayed(): + b.click() + break + time.sleep(10.) + except Exception as e: + logger.warning('Got an error {}'.format(e)) + return False + + deadline = time.perf_counter() + timeout + while time.perf_counter() < deadline: + try: + self.driver.find_element(By.TAG_NAME, value='video') + return True + except selenium.common.exceptions.NoSuchElementException: + continue + return False + + def continue_watching(self, timeout=5.): + + try: + for b in self.driver.find_elements(By.TAG_NAME, value='button'): + if b.accessible_name == 'OK': + b.click() + break + time.sleep(3.) + for b in self.driver.find_elements(By.TAG_NAME, value='button'): + if b.accessible_name == '閉じる': + b.click() + break + time.sleep(3.) + for b in self.driver.find_elements(By.TAG_NAME, value='button'): + if b.accessible_name == '今なに見てる?' and b.is_displayed(): + b.click() + break + time.sleep(10.) + except Exception as e: + logger.warn('Got an error: {}'.format(e)) + return False + + deadline = time.perf_counter() + timeout + while time.perf_counter() < deadline: + try: + self.driver.find_element(By.TAG_NAME, value='video') + return True + except selenium.common.exceptions.NoSuchElementException: + continue + return False + + def get_current_image(self): + + try: + raw_data_image = self.driver.find_element( + By.TAG_NAME, value='video').screenshot_as_base64 + cv_image_array_rgb = imread(io.BytesIO(base64.b64decode(raw_data_image))) + return cv_image_array_rgb + except (selenium.common.exceptions.NoSuchElementException, + selenium.common.exceptions.StaleElementReferenceException): + return None + + def get_error_message(self): + + try: + self.driver.find_element(By.CLASS_NAME, value='jss755').text + except selenium.common.exceptions.NoSuchElementException: + return None diff --git a/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/geckodriver.log b/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/geckodriver.log new file mode 100644 index 0000000000..850060491f --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/python/aibo_selenium_ros/geckodriver.log @@ -0,0 +1,71 @@ +1694083040310 geckodriver INFO Listening on 127.0.0.1:36887 +1694083040823 mozrunner::runner INFO Running command: MOZ_CRASHREPORTER="1" MOZ_CRASHREPORTER_NO_REPORT="1" MOZ_CRASHREPORTER_SHUTDOWN="1" MOZ_NO_REMOTE="1" "/usr ... 15" "--remote-debugging-port" "45867" "--remote-allow-hosts" "localhost" "-no-remote" "-profile" "/tmp/rust_mozprofileNIU3Ix" +console.warn: services.settings: Ignoring preference override of remote settings server +console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment +ATTENTION: default value of option mesa_glthread overridden by environment. +1694083041907 Marionette INFO Marionette enabled +console.error: "Warning: unrecognized command line flag" "-disable-blink-features" +console.error: "Warning: unrecognized command line flag" "-user-agent" +ATTENTION: default value of option mesa_glthread overridden by environment. +1694083042013 Marionette INFO Listening on port 36857 +Read port: 36857 +WebDriver BiDi listening on ws://127.0.0.1:45867 +1694083042080 RemoteAgent WARN TLS certificate errors will be ignored for this session +console.error: ({}) +DevTools listening on ws://127.0.0.1:45867/devtools/browser/fb9d5f44-68a9-485a-83d3-0c51e7b7985c +JavaScript warning: https://myaibo.aibo.sony.jp/assets/bundle.js, line 48: Script terminated by timeout at: +xa@https://myaibo.aibo.sony.jp/assets/bundle.js:48:90654 +Rn@https://myaibo.aibo.sony.jp/assets/bundle.js:48:30478 + +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: unreachable code after return statement +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +console.warn: LoginRecipes: "Falling back to a synchronous message for: https://acm.account.sony.com." +console.warn: services.settings: Ignoring preference override of remote settings server +console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment +console.error: (new TypeError("lazy.AsyncShutdown.profileBeforeChange is undefined", "resource://services-settings/Database.sys.mjs", 593)) +console.error: "update.locale" " file doesn't exist in either the application or GRE directories" +JavaScript warning: https://my.account.sony.com/central/signin/c9b433609b383b6d7147ab7a2f0dcff63b47f87f/assets/vendor-889503f9bf182b98c91436fde9bbd8f8.js, line 8160: unreachable code after return statement +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: unreachable code after return statement +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +console.warn: LoginRecipes: "Falling back to a synchronous message for: https://my.account.sony.com." +JavaScript warning: https://client-api.arkoselabs.com/cdn/fc/js/6af2c0d87b9879cbf3365be1a208293f84d37b1e/standard/funcaptcha_api.js?onload=loadChallenge, line 2: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +console.warn: services.settings: Ignoring preference override of remote settings server +console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment +console.error: (new TypeError("lazy.AsyncShutdown.profileBeforeChange is undefined", "resource://services-settings/Database.sys.mjs", 593)) +1694083129915 Marionette INFO Stopped listening on port 36857 +1694083135711 geckodriver INFO Listening on 127.0.0.1:53347 +1694083136220 mozrunner::runner INFO Running command: MOZ_CRASHREPORTER="1" MOZ_CRASHREPORTER_NO_REPORT="1" MOZ_CRASHREPORTER_SHUTDOWN="1" MOZ_NO_REMOTE="1" "/usr ... 15" "--remote-debugging-port" "55713" "--remote-allow-hosts" "localhost" "-no-remote" "-profile" "/tmp/rust_mozprofile1dCaPK" +console.warn: services.settings: Ignoring preference override of remote settings server +console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment +ATTENTION: default value of option mesa_glthread overridden by environment. +1694083137339 Marionette INFO Marionette enabled +console.error: "Warning: unrecognized command line flag" "-disable-blink-features" +console.error: "Warning: unrecognized command line flag" "-user-agent" +ATTENTION: default value of option mesa_glthread overridden by environment. +1694083137446 Marionette INFO Listening on port 34359 +WebDriver BiDi listening on ws://127.0.0.1:55713 +Read port: 34359 +1694083137726 RemoteAgent WARN TLS certificate errors will be ignored for this session +console.error: ({}) +DevTools listening on ws://127.0.0.1:55713/devtools/browser/c2791957-3a00-49d4-88fd-d91eee6ed79b +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: unreachable code after return statement +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://acm.account.sony.com/5h7VmhV-da/NQ/qVI8FFHp/G77rLc0Sp0EOuE/IVIdI3dzAQ/fjM1IB/AIaAQ, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +console.warn: LoginRecipes: "Falling back to a synchronous message for: https://acm.account.sony.com." +console.warn: services.settings: Ignoring preference override of remote settings server +console.warn: services.settings: Allow by setting MOZ_REMOTE_SETTINGS_DEVTOOLS=1 in the environment +console.error: (new TypeError("lazy.AsyncShutdown.profileBeforeChange is undefined", "resource://services-settings/Database.sys.mjs", 593)) +console.error: "update.locale" " file doesn't exist in either the application or GRE directories" +JavaScript warning: https://my.account.sony.com/central/signin/c9b433609b383b6d7147ab7a2f0dcff63b47f87f/assets/vendor-889503f9bf182b98c91436fde9bbd8f8.js, line 8160: unreachable code after return statement +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: unreachable code after return statement +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +JavaScript warning: https://my.account.sony.com/9hRawg/tKNSFk/ubOB/44hKfq/Q3/cu5tLDf4mVY9OY/UEZsSQ/MXwZ/eyZbQQQB, line 1: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +console.warn: LoginRecipes: "Falling back to a synchronous message for: https://my.account.sony.com." +JavaScript warning: https://client-api.arkoselabs.com/cdn/fc/js/6af2c0d87b9879cbf3365be1a208293f84d37b1e/standard/funcaptcha_api.js?onload=loadChallenge, line 2: WEBGL_debug_renderer_info is deprecated in Firefox and will be removed. Please use RENDERER. +1694083508103 Marionette INFO Stopped listening on port 34359 diff --git a/jsk_aibo_robot/aibo_selenium_ros/requirements.txt b/jsk_aibo_robot/aibo_selenium_ros/requirements.txt new file mode 100644 index 0000000000..a0cd31f6c6 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/requirements.txt @@ -0,0 +1,3 @@ +selenium==4.9.0 +imageio +undetected-chromedriver==3.5.0 diff --git a/jsk_aibo_robot/aibo_selenium_ros/setup.py b/jsk_aibo_robot/aibo_selenium_ros/setup.py new file mode 100644 index 0000000000..f804665b04 --- /dev/null +++ b/jsk_aibo_robot/aibo_selenium_ros/setup.py @@ -0,0 +1,9 @@ +from distutils.core import setup +from catkin_pkg.python_setup import generate_distutils_setup + +d = generate_distutils_setup( + packages=['aibo_selenium_ros'], + package_dir={'': 'python'} +) + +setup(**d)