diff --git a/xfinity-usage/CHANGELOG.md b/xfinity-usage/CHANGELOG.md index 96134eb..7e726c2 100644 --- a/xfinity-usage/CHANGELOG.md +++ b/xfinity-usage/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.2.2 + +- Run script headed using xvfb-run + ## 0.1.2.1 - Updated StealthConfig to recommended settings diff --git a/xfinity-usage/Dockerfile b/xfinity-usage/Dockerfile index 81fc9f5..eb755e3 100755 --- a/xfinity-usage/Dockerfile +++ b/xfinity-usage/Dockerfile @@ -63,6 +63,8 @@ RUN \ libfontconfig1 \ libdbus-1-3 \ python3-pip \ + xauth \ + xvfb \ \ && c_rehash \ \ diff --git a/xfinity-usage/config.yaml b/xfinity-usage/config.yaml index 15ba076..b844cf1 100755 --- a/xfinity-usage/config.yaml +++ b/xfinity-usage/config.yaml @@ -1,7 +1,7 @@ name: "Xfinity Internet Usage" description: "Get Xfinity Internet Usage Data" url: "https://github.com/thor0215/hassio-xfinity-usage" -version: "0.1.2.1" +version: "0.1.2.2" image: "ghcr.io/thor0215/hassio-xfinity-usage-{arch}" slug: "xfinity-usage" init: false @@ -32,6 +32,7 @@ options: mqtt_host: core-mosquitto mqtt_port: 1883 mqtt_username: addons + headless: false schema: xfinity_username: str @@ -47,3 +48,4 @@ schema: mqtt_raw_usage: bool? #profile_cleanup: bool? debug_support: bool? + headless: bool? diff --git a/xfinity-usage/run.sh b/xfinity-usage/run.sh index e17b36c..7632171 100755 --- a/xfinity-usage/run.sh +++ b/xfinity-usage/run.sh @@ -2,7 +2,7 @@ # shellcheck shell=bash # shellcheck disable=SC1091 -export HEADLESS=True +#export HEADLESS=False export ABORT_ROUTE=True # Remove debug log file on every start @@ -44,6 +44,7 @@ if [ $BYPASS = "0" ]; then [[ $(bashio::config "mqtt_raw_usage") != null ]] && export MQTT_RAW_USAGE=$(bashio::config "mqtt_raw_usage") #[[ $(bashio::config "profile_cleanup") != null ]] && export PROFILE_CLEANUP=$(bashio::config "profile_cleanup") [[ $(bashio::config "debug_support") != null ]] && export DEBUG_SUPPORT=$(bashio::config "debug_support") + [[ $(bashio::config "headless") != null ]] && export HEADLESS=$(bashio::config "headless") if [ "${LOG_LEVEL}" == "debug" ] || [ "${LOG_LEVEL}" == "debug_support" ]; then @@ -52,13 +53,24 @@ if [ $BYPASS = "0" ]; then ls -al /config fi - # Let bash handle the polling rate - while timeout -s INT -k 30s $(bashio::config "polling_rate") python3 -Wignore /xfinity_usage_addon.py; do - bashio::log.info "Sleeping for $(bashio::config "polling_rate") seconds" - sleep $(bashio::config "polling_rate")s; - done + if [ $HEADLESS == "True" ]; then + # Let bash handle the polling rate + while timeout -s INT -k 30s $(bashio::config "polling_rate") python3 -Wignore /xfinity_usage_addon.py; do + bashio::log.info "Sleeping for $(bashio::config "polling_rate") seconds" + sleep $(bashio::config "polling_rate")s; + done + else + # Let bash handle the polling rate + while timeout -s INT -k 30s $(bashio::config "polling_rate") xvfb-run python3 -Wignore /xfinity_usage_addon.py; do + bashio::log.info "Sleeping for $(bashio::config "polling_rate") seconds" + sleep $(bashio::config "polling_rate")s; + done + fi else - #xvfb-run python3 -Wignore xfinity_usage_addon.py # Headed mode - python3 -Wignore /xfinity_usage_addon.py + if [ $HEADLESS == "True" ]; then + python3 -Wignore /xfinity_usage_addon.py + else + xvfb-run python3 -Wignore xfinity_usage_addon.py # Headed mode + fi fi diff --git a/xfinity-usage/translations/en.yaml b/xfinity-usage/translations/en.yaml index 1a240f9..a16844c 100644 --- a/xfinity-usage/translations/en.yaml +++ b/xfinity-usage/translations/en.yaml @@ -47,4 +47,8 @@ configuration: debug_support: name: Debug Support description: >- - Script will output screenshots and extra logging, but exit after first attempt. (optional) \ No newline at end of file + Script will output screenshots and extra logging, but exit after first attempt. (optional) + headless: + name: Run Playwright Headless + description: >- + Run script headless (optional) \ No newline at end of file diff --git a/xfinity-usage/xfinity_usage_addon.py b/xfinity-usage/xfinity_usage_addon.py index 9a1812d..9094ecc 100644 --- a/xfinity-usage/xfinity_usage_addon.py +++ b/xfinity-usage/xfinity_usage_addon.py @@ -51,10 +51,11 @@ #AUTH_URL = 'https://oauth.xfinity.com/oauth/authorize?response_type=token&prompt=select_billing_account&redirect_uri=https%3A%2F%2Fwww.xfinity.com%2Fpost-auth&client_id=shoplearn-web&state=https%3A%2F%2Fwww.xfinity.com%2Fauth' #AUTH_URL = 'https://customer.xfinity.com/billing' #AUTH_URL = 'https://customer.xfinity.com/settings' -AUTH_URL = 'https://www.xfinity.com/learn/internet-service/auth' +#AUTH_URL = 'https://www.xfinity.com/learn/internet-service/auth' +#AUTH_URL = 'https://oauth.xfinity.com/oauth/authorize?response_type=token&prompt=select_billing_account%20none&redirect_uri=https%3A%2F%2Fwww.xfinity.com%2Fxfinity-learn-ui%2Ftoken.html&client_id=shoplearn-web&state=https%3A%2F%2Fwww.xfinity.com%2Flearn%2Finternet-service%2Fauth&nonce=XniaT.-DFCO~7vUs' LOGIN_URL = 'https://login.xfinity.com/login' LOGIN_PAGE_TITLE = 'Sign in to Xfinity' -#LOGOUT_URL = 'https://www.xfinity.com/overview' +AUTH_URL = 'https://www.xfinity.com/overview' LOGOUT_URL = 'https://oauth.xfinity.com/oauth/sp-logout?client_id=shoplearn-web' USAGE_JSON_URL = ['https://api.sc.xfinity.com/session/csp/selfhelp/account/me/services/internet/usage', @@ -1111,39 +1112,48 @@ async def goto_logout(self, _quiet = False) -> None: """ async def get_authenticated(self) -> None: - await self.page.goto(AUTH_URL) - logger.info(f"Loading Xfinity Authentication (URL: {parse_url(self.page.url)})") + await self.page.goto(AUTH_URL, wait_until='networkidle') + logger.info(f"Loading Xfinity (URL: {parse_url(self.page.url)})") _title = await self.get_page_title() _state = await self.page.locator('xc-header').get_attribute('state') - # xc-header[state="authenticated"] - if _title == AUTH_PAGE_TITLE and \ - _state != "authenticated": - await self.page.close() - await self.goto_logout() + if await self.page.locator('li.xc-header--avatar-menu-toggle').locator('button[aria-label="Account"]').is_visible(): + await self.page.locator('li.xc-header--avatar-menu-toggle').locator('button[aria-label="Account"]').click() + await get_slow_down_login() + + if await self.page.locator('div.xc-header--signin-container--unauthenticated').locator('a.xc-header--signin-link', has_text='Sign In').is_visible(): + await self.page.locator('div.xc-header--signin-container--unauthenticated').locator('a.xc-header--signin-link', has_text='Sign In').press("Enter") + logger.info(f"Loading Xfinity Authentication") - _start_time = time.time() - while(self.is_session_active is not True): - - if bool(self.plan_details_data) and bool(self.usage_details_data): - self.is_session_active = True - else: - await self.check_for_authentication_errors() - await self.debug_support() - await self.check_authentication_form() - await self.debug_support() - await get_slow_down_login() - - if time.time()-_start_time > PAGE_TIMEOUT and self.is_session_active is not True: - _title = await self.get_page_title() - if _title == AUTH_PAGE_TITLE: + """ + # xc-header[state="authenticated"] + if _title == AUTH_PAGE_TITLE and \ + _state == "authenticated": + await self.page.close() await self.goto_logout() - await self.context.clear_cookies() - raise AssertionError(f"Login Failed: Logging out and clearing cookies") - - if self.is_session_active: - await self.page.wait_for_load_state('networkidle') - await self.debug_support() - await self.page.close() + """ + _start_time = time.time() + while(self.is_session_active is not True): + + if bool(self.plan_details_data) and bool(self.usage_details_data): + self.is_session_active = True + else: + await self.check_for_authentication_errors() + await self.debug_support() + await self.check_authentication_form() + await self.debug_support() + await get_slow_down_login() + + if time.time()-_start_time > PAGE_TIMEOUT and self.is_session_active is not True: + _title = await self.get_page_title() + if _title == AUTH_PAGE_TITLE: + await self.goto_logout() + await self.context.clear_cookies() + raise AssertionError(f"Login Failed: Logging out and clearing cookies") + + if self.is_session_active: + await self.page.wait_for_load_state('networkidle') + await self.debug_support() + await self.page.close() @@ -1353,8 +1363,16 @@ async def run(self) -> None: # Needed if using persistent profiles #await self.goto_logout(True) #await self.debug_support() - await self.get_authenticated() - + _start_time = time.time() + while(self.is_session_active is not True): + await self.get_authenticated() + if time.time()-_start_time > PAGE_TIMEOUT and self.is_session_active is not True: + _title = await self.get_page_title() + if _title == AUTH_PAGE_TITLE: + await self.goto_logout() + await self.context.clear_cookies() + raise AssertionError(f"Login Failed: Logging out and clearing cookies") + # If we do not have the plan and usage data, success and lets process it if not bool(self.plan_details_data) and not bool(self.usage_details_data): await self.get_usage_data()