diff --git a/.gitignore b/.gitignore index a6b45f9..6f4f743 100644 --- a/.gitignore +++ b/.gitignore @@ -168,3 +168,4 @@ github_support/ get_easyprivacy_xfinity_block_list.sh .github/dependabot.yml.bak +xfinity-usage/xfinity_start.sh diff --git a/xfinity-usage/CHANGELOG.md b/xfinity-usage/CHANGELOG.md index c312e46..e3cba42 100644 --- a/xfinity-usage/CHANGELOG.md +++ b/xfinity-usage/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 0.0.12.8 + +- Switched User Agent to Android. Set Android (10-13) and Firefox (120-124) versions randomly. + - Setting Firefox user agent to use a lower version causes less login/Akamai errors +- No longer using browser persistent storage. I didn't really help anything +- Added exit code enum class to better track exit codes +- Changed LOGLEVEL Addon variable to LOG_LEVEL so it links to bashio's log level variable +- Improve the usage of the addon by non-hassio consumers (docker and kubernetes) [#36](https://github.com/thor0215/hassio-xfinity-usage/issues/36) + - run.sh will only execute bashio calls if it detects bashio + - xfinity_usage_addon.py script will only do polling if bashio was detected + +- Dependency updates + - Update Debian base image to v12.7 + - Bump playwright from 1.46.0 to 1.47.0 in /xfinity-usage + - Bump pyee from 11.1.0 to 12.0.0 in /xfinity-usage + - Removed greenlet requirement, Playwright will install the version it supports. This was causing Dependabot to create unnecessary Pull Requests + ## 0.0.12.7.2.2 - Second attempt to Fix MQTT Auto discovery issue when MQTT device has null values. Issue [#22](https://github.com/thor0215/hassio-xfinity-usage/issues/22) diff --git a/xfinity-usage/Dockerfile b/xfinity-usage/Dockerfile index bf24800..7cd8df1 100755 --- a/xfinity-usage/Dockerfile +++ b/xfinity-usage/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=debian:12.6-slim +ARG BUILD_FROM=debian:12.7-slim # hadolint ignore=DL3006 FROM ${BUILD_FROM} @@ -38,6 +38,31 @@ RUN \ jq=1.6-2.1 \ tzdata=2024a-0+deb12u1 \ xz-utils=5.4.1-0.2 \ + libxcb-shm0\ + libx11-xcb1 \ + libx11-6 \ + libxcb1 \ + libxext6 \ + libxrandr2 \ + libxcomposite1 \ + libxcursor1 \ + libxdamage1 \ + libxfixes3 \ + libxi6 \ + libgtk-3-0 \ + libpangocairo-1.0-0 \ + libpango-1.0-0 \ + libatk1.0-0 \ + libcairo-gobject2 \ + libcairo2 \ + libgdk-pixbuf-2.0-0 \ + libglib2.0-0 \ + libasound2 \ + libxrender1 \ + libfreetype6 \ + libfontconfig1 \ + libdbus-1-3 \ + python3-pip \ \ && c_rehash \ \ @@ -121,36 +146,9 @@ LABEL \ org.opencontainers.image.revision=${BUILD_REF} \ org.opencontainers.image.version=${BUILD_VERSION} -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - libxcb-shm0\ - libx11-xcb1 \ - libx11-6 \ - libxcb1 \ - libxext6 \ - libxrandr2 \ - libxcomposite1 \ - libxcursor1 \ - libxdamage1 \ - libxfixes3 \ - libxi6 \ - libgtk-3-0 \ - libpangocairo-1.0-0 \ - libpango-1.0-0 \ - libatk1.0-0 \ - libcairo-gobject2 \ - libcairo2 \ - libgdk-pixbuf-2.0-0 \ - libglib2.0-0 \ - libasound2 \ - libxrender1 \ - libfreetype6 \ - libfontconfig1 \ - libdbus-1-3 \ - python3-pip - # Allow playwright to run in headed mode -#RUN apt-get install -y --no-install-recommends \ +#RUN apt-get update \ +# && apt-get install -y --no-install-recommends \ # xvfb COPY requirements.txt / @@ -166,6 +164,18 @@ COPY run.sh / COPY xfinity_usage_addon.py / RUN chmod a+x /run.sh - CMD [ "/run.sh" ] +# Bits for running as xfinity user +#RUN useradd -mr -d /var/cache/xfinity -s /sbin/nologin xfinity +#COPY run.sh /var/cache/xfinity +#COPY xfinity_usage_addon.py /var/cache/xfinity +#RUN chmod a+x /var/cache/xfinity/run.sh +#RUN chown xfinity:xfinity -R /var/cache/xfinity + +#USER xfinity +#RUN cd /var/cache/xfinity && playwright install firefox + +#USER root +#CMD [ "/var/cache/xfinity/run.sh" ] + diff --git a/xfinity-usage/build.yaml b/xfinity-usage/build.yaml index 81b7c03..bab8b1d 100644 --- a/xfinity-usage/build.yaml +++ b/xfinity-usage/build.yaml @@ -1,6 +1,6 @@ --- build_from: - aarch64: arm64v8/debian:12.6-slim - amd64: amd64/debian:12.6-slim + aarch64: arm64v8/debian:12.7-slim + amd64: amd64/debian:12.7-slim codenotary: signer: codenotary@frenck.dev \ No newline at end of file diff --git a/xfinity-usage/config.yaml b/xfinity-usage/config.yaml index f7442b3..4ca6cd0 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.0.12.7.2.2" +version: "0.0.12.8" image: "ghcr.io/thor0215/hassio-xfinity-usage-{arch}" slug: "xfinity-usage" init: false @@ -27,7 +27,7 @@ options: xfinity_password: page_timeout: 60 polling_rate: 900 - loglevel: info + log_level: info mqtt_enabled: false mqtt_host: core-mosquitto mqtt_port: 1883 @@ -38,7 +38,7 @@ schema: xfinity_password: password page_timeout: int(15,)? polling_rate: int(30,)? - loglevel: list(info|debug|debug_support)? + log_level: list(info|debug|debug_support)? mqtt_enabled: bool? mqtt_host: str? mqtt_port: port? diff --git a/xfinity-usage/requirements.txt b/xfinity-usage/requirements.txt index b19270d..5a462e7 100644 --- a/xfinity-usage/requirements.txt +++ b/xfinity-usage/requirements.txt @@ -1,9 +1,8 @@ typing-extensions==4.12.2 - pyee==11.1.0 - greenlet==3.0.3 + pyee==12.0.0 tenacity==9.0.0 requests==2.32.3 PyJWT==2.9.0 paho-mqtt==2.1.0 - playwright==1.46.0 + playwright==1.47.0 diff --git a/xfinity-usage/run.sh b/xfinity-usage/run.sh index 48757c5..b4cd086 100755 --- a/xfinity-usage/run.sh +++ b/xfinity-usage/run.sh @@ -2,38 +2,43 @@ # shellcheck shell=bash # shellcheck disable=SC1091 -export XFINITY_USERNAME=$(bashio::config "xfinity_username") -export XFINITY_PASSWORD=$(bashio::config "xfinity_password") -export PAGE_TIMEOUT=$(bashio::config "page_timeout") -export LOGLEVEL=$(bashio::config "loglevel") -export POLLING_RATE=$(bashio::config "polling_rate") -export BASHIO_SUPERVISOR_API="${__BASHIO_SUPERVISOR_API}" -export BASHIO_SUPERVISOR_TOKEN="${__BASHIO_SUPERVISOR_TOKEN}" -if bashio::services.available 'mqtt'; then - bashio::log.green "---" - bashio::log.yellow "MQTT addon is active on your system!" - bashio::log.yellow "Add the MQTT details below to the addon configuration :" - bashio::log.blue "MQTT user : $(bashio::services "mqtt" "username")" - bashio::log.blue "MQTT password : $(bashio::services "mqtt" "password")" - bashio::log.blue "MQTT Hostname : $(bashio::services "mqtt" "host")" - bashio::log.blue "MQTT Port : $(bashio::services "mqtt" "port")" - bashio::log.green "---" -fi +# Issue #36 add support for non Home Assistant deployments +if [[ -n ${__BASHIO_SUPERVISOR_API} ]]; then + export HASSIO=true + export XFINITY_USERNAME=$(bashio::config "xfinity_username") + export XFINITY_PASSWORD=$(bashio::config "xfinity_password") + export PAGE_TIMEOUT=$(bashio::config "page_timeout") + export LOG_LEVEL=$(bashio::config "log_level") + export POLLING_RATE=$(bashio::config "polling_rate") + export BASHIO_SUPERVISOR_API="${__BASHIO_SUPERVISOR_API}" + export BASHIO_SUPERVISOR_TOKEN="${__BASHIO_SUPERVISOR_TOKEN}" + + if bashio::services.available 'mqtt'; then + bashio::log.green "---" + bashio::log.yellow "MQTT addon is active on your system!" + bashio::log.yellow "Add the MQTT details below to the addon configuration :" + bashio::log.blue "MQTT user : $(bashio::services "mqtt" "username")" + bashio::log.blue "MQTT password : $(bashio::services "mqtt" "password")" + bashio::log.blue "MQTT Hostname : $(bashio::services "mqtt" "host")" + bashio::log.blue "MQTT Port : $(bashio::services "mqtt" "port")" + bashio::log.green "---" + fi -[[ $(bashio::config "mqtt_enabled") != null ]] && export MQTT_SERVICE=$(bashio::config "mqtt_enabled") -[[ $(bashio::config "mqtt_username") != null ]] && export MQTT_USERNAME=$(bashio::config "mqtt_username") -[[ $(bashio::config "mqtt_password") != null ]] && export MQTT_PASSWORD=$(bashio::config "mqtt_password") -[[ $(bashio::config "mqtt_password") == null ]] && export MQTT_PASSWORD=$(bashio::services "mqtt" "password") -[[ $(bashio::config "mqtt_host") != null ]] && export MQTT_HOST=$(bashio::config "mqtt_host") -[[ $(bashio::config "mqtt_port") != null ]] && export MQTT_PORT=$(bashio::config "mqtt_port") + [[ $(bashio::config "mqtt_enabled") != null ]] && export MQTT_SERVICE=$(bashio::config "mqtt_enabled") + [[ $(bashio::config "mqtt_username") != null ]] && export MQTT_USERNAME=$(bashio::config "mqtt_username") + [[ $(bashio::config "mqtt_password") != null ]] && export MQTT_PASSWORD=$(bashio::config "mqtt_password") + [[ $(bashio::config "mqtt_password") == null ]] && export MQTT_PASSWORD=$(bashio::services "mqtt" "password") + [[ $(bashio::config "mqtt_host") != null ]] && export MQTT_HOST=$(bashio::config "mqtt_host") + [[ $(bashio::config "mqtt_port") != null ]] && export MQTT_PORT=$(bashio::config "mqtt_port") -if [ "${LOGLEVEL}" == "debug" ] || [ "${LOGLEVEL}" == "debug_support" ]; then - python3 --version - python3 -m pip list - ls -al /config + if [ "${LOG_LEVEL}" == "debug" ] || [ "${LOG_LEVEL}" == "debug_support" ]; then + python3 --version + python3 -m pip list + ls -al /config + fi fi #xvfb-run python3 -Wignore xfinity_usage_addon.py # Headed mode -python3 -Wignore xfinity_usage_addon.py +python3 -Wignore /xfinity_usage_addon.py diff --git a/xfinity-usage/translations/en.yaml b/xfinity-usage/translations/en.yaml index 1c0fa7a..648373b 100644 --- a/xfinity-usage/translations/en.yaml +++ b/xfinity-usage/translations/en.yaml @@ -12,7 +12,7 @@ configuration: name: Polling Rate (Seconds) description: >- This setting will change how often the addon will poll for new usage data - loglevel: + log_level: name: Log level description: >- Sets the verbosity of log output. diff --git a/xfinity-usage/xfinity_usage_addon.py b/xfinity-usage/xfinity_usage_addon.py index 84d8e9d..6b56560 100644 --- a/xfinity-usage/xfinity_usage_addon.py +++ b/xfinity-usage/xfinity_usage_addon.py @@ -1,24 +1,25 @@ -import os +import base64 +import fnmatch import json +import jwt import logging -import shutil -import requests -import urllib.parse -import fnmatch -import time +import os import random -import base64 -import jwt import re -import textwrap +import requests +import shutil import socket import ssl +import textwrap +import time +import urllib.parse from datetime import datetime +from enum import Enum +from tenacity import stop_after_attempt, wait_exponential, retry, before_sleep_log from time import sleep +from paho.mqtt import client as mqtt from pathlib import Path -from tenacity import stop_after_attempt, wait_exponential, retry, before_sleep_log from playwright.sync_api import Playwright, Route, Response, Request, Frame, Page, sync_playwright, expect -from paho.mqtt import client as mqtt def get_current_unix_epoch() -> float: return time.time() @@ -52,20 +53,28 @@ def ordinal(n): Node.js v18.16.0 """ - -POLLING_RATE = float(os.environ.get('POLLING_RATE', "300.0")) -PAGE_TIMEOUT = int(os.environ.get('PAGE_TIMEOUT', "60")) -MQTT_SERVICE = json.loads(os.getenv('MQTT_SERVICE').lower()) # Convert MQTT_SERVICE string into boolean -LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper().split('_')[0] -SUPPORT = False -if len(os.environ.get('LOGLEVEL', 'INFO').upper().split('_')) > 1 and 'SUPPORT' == os.environ.get('LOGLEVEL', 'INFO').upper().split('_')[1] : SUPPORT = True +SLOW_DOWN_MIN = 0.5 +SLOW_DOWN_MAX = 1.2 + +# HASSIO controls whether script will do polling or not +# Issue #36 add support for non Home Assistant deployments +HASSIO = json.loads(os.environ.get('HASSIO', 'false').lower()) # Convert HASSIO string into boolean +POLLING_RATE = float(os.environ.get('POLLING_RATE', 300.0)) +PAGE_TIMEOUT = int(os.environ.get('PAGE_TIMEOUT', 60)) +MQTT_SERVICE = json.loads(os.environ.get('MQTT_SERVICE', 'false').lower()) # Convert MQTT_SERVICE string into boolean +LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO').upper().split('_')[0] +DEBUG_SUPPORT = False +if len(os.environ.get('LOG_LEVEL', 'INFO').upper().split('_')) > 1 and 'DEBUG_SUPPORT' == os.environ.get('LOG_LEVEL', 'INFO').upper().split('_')[1] : DEBUG_SUPPORT = True SENSOR_BACKUP = '/config/.sensor-backup' mqtt_client = None debug_logger_file = '/config/xfinity.log' profile_paths = ['/config/profile_mobile','/config/profile_linux','/config/profile_win'] -FIREFOX_MIN_VERSION = 125 -FIREFOX_MAX_VERSION = 130 +ANDROID_MIN_VERSION = 10 +ANDROID_MAX_VERSION = 13 +FIREFOX_MIN_VERSION = 120 +FIREFOX_MAX_VERSION = 124 + # Remove browser profile path upon startup for profile_path in profile_paths: @@ -80,16 +89,16 @@ def ordinal(n): logging.basicConfig(format='%(asctime)s.%(msecs)03d %(levelname)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S') logger = logging.getLogger(__name__) -logger.setLevel(LOGLEVEL) +logger.setLevel(LOG_LEVEL) formatter = logging.Formatter(fmt='%(asctime)s.%(msecs)03d %(levelname)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S') -if LOGLEVEL == 'DEBUG': +if LOG_LEVEL == 'DEBUG': file_handler = logging.FileHandler(debug_logger_file,mode='w') file_handler.setFormatter(formatter) logger.addHandler(file_handler) - if SUPPORT: + if DEBUG_SUPPORT: debug_support_logger = logging.getLogger(__name__ + '.file_logger') debug_support_logger.addHandler(file_handler) debug_support_logger.propagate = False @@ -107,6 +116,14 @@ def is_mqtt_available() -> bool: else: return False +class exit_code(Enum): + SUCCESS = 0 + MISSING_LOGIN_CONFIG = 80 + MISSING_MQTT_CONFIG = 81 + TWO_STEP_VERIFICATION = 97 + MAIN_EXCEPTION = 98 + DEBUG_SUPPORT = 99 + class XfinityMqtt (): def __init__(self, max_retries=5, retry_delay=5): @@ -156,7 +173,7 @@ def __init__(self, max_retries=5, retry_delay=5): self.client.username_pw_set(self.mqtt_username, self.mqtt_password) else: logger.error("No MQTT configuration specified") - exit(98) + exit(exit_code.MISSING_MQTT_CONFIG.value) self.client = self.connect_mqtt() @@ -299,16 +316,16 @@ def __init__(self, playwright: Playwright) -> None: self.pending_requests = [] self.slow_down_login = True self.FIREFOX_VERSION = str(random.randint(FIREFOX_MIN_VERSION, FIREFOX_MAX_VERSION)) + self.ANDROID_VERSION = str(random.randint(ANDROID_MIN_VERSION, ANDROID_MAX_VERSION)) - - if SUPPORT: self.support_page_hash = int; self.support_page_screenshot_hash = int + if DEBUG_SUPPORT: self.support_page_hash = int; self.support_page_screenshot_hash = int if os.getenv('XFINITY_PASSWORD') and os.getenv('XFINITY_USERNAME'): self.xfinity_username = os.getenv('XFINITY_USERNAME') self.xfinity_password = os.getenv('XFINITY_PASSWORD') else: logger.error("No Username or Password specified") - exit(99) + exit(exit_code.MISSING_LOGIN_CONFIG.value) if is_mqtt_available() is False and os.path.isfile(SENSOR_BACKUP) and os.path.getsize(SENSOR_BACKUP): with open(SENSOR_BACKUP, 'r') as file: @@ -325,13 +342,13 @@ def __init__(self, playwright: Playwright) -> None: self.webdriver_script = "delete Object.getPrototypeOf(navigator).webdriver" #self.browser = playwright.firefox.launch(headless=False,slow_mo=1000,firefox_user_prefs=self.firefox_user_prefs) - #self.browser = playwright.firefox.launch(headless=True,firefox_user_prefs=self.firefox_user_prefs) - #self.context = self.browser.new_context(**self.device) + self.browser = playwright.firefox.launch(headless=True,firefox_user_prefs=self.firefox_user_prefs) + self.context = self.browser.new_context(**self.device) #self.context = playwright.firefox.launch_persistent_context(profile_path,headless=False,firefox_user_prefs=self.firefox_user_prefs,**self.device) #self.context = playwright.firefox.launch_persistent_context(profile_path,headless=False,firefox_user_prefs=self.firefox_user_prefs,**self.device) - self.context = playwright.firefox.launch_persistent_context(self.profile_path,headless=True,firefox_user_prefs=self.firefox_user_prefs,**self.device) + #self.context = playwright.firefox.launch_persistent_context(self.profile_path,headless=True,firefox_user_prefs=self.firefox_user_prefs,**self.device) # Block unnecessary requests @@ -350,7 +367,7 @@ def __init__(self, playwright: Playwright) -> None: # Help reduce bot detection self.page.add_init_script(self.webdriver_script) - if SUPPORT and \ + if DEBUG_SUPPORT and \ os.path.exists('/config/'): self.page.on("console", lambda consolemessage: debug_support_logger.debug(f"Console Message: {consolemessage.text}")) self.page.on("pageerror", self.check_pageerror) @@ -374,32 +391,60 @@ def akamai_sleep(self): def two_step_verification_handler(self): logger.error(f"Two-Step Verification is turned on. Exiting...") - exit(95) + exit(exit_code.TWO_STEP_VERIFICATION.value) + def get_slow_down_login(self) -> None: + if self.slow_down_login: + time.sleep(random.uniform(SLOW_DOWN_MIN, SLOW_DOWN_MAX)) + + def get_browser_device(self) -> dict: # Help reduce bot detection device_choices = [] device_choices.append({ - "user_agent": "Mozilla/5.0 (Android 13; Mobile; rv:"+self.FIREFOX_VERSION+".0) Gecko/"+self.FIREFOX_VERSION+".0 Firefox/"+self.FIREFOX_VERSION+".0", + "user_agent": "Mozilla/5.0 (Android "+self.ANDROID_VERSION+"; Mobile; rv:"+self.FIREFOX_VERSION+".0) Gecko/"+self.FIREFOX_VERSION+".0 Firefox/"+self.FIREFOX_VERSION+".0", + "screen": {"width": 414,"height": 896}, "viewport": {"width": 414,"height": 896}, + "device_scale_factor": 2, "has_touch": True + }) + """ + device_choices.append({ + "user_agent": "Mozilla/5.0 (Android 12; Mobile; rv:"+self.FIREFOX_VERSION+".0) Gecko/"+self.FIREFOX_VERSION+".0 Firefox/"+self.FIREFOX_VERSION+".0", + "screen": {"width": 414,"height": 896}, "viewport": {"width": 414,"height": 896}, + "device_scale_factor": 2, "has_touch": True + }) + device_choices.append({ + "user_agent": "Mozilla/5.0 (Android 11; Mobile; rv:"+self.FIREFOX_VERSION+".0) Gecko/"+self.FIREFOX_VERSION+".0 Firefox/"+self.FIREFOX_VERSION+".0", "screen": {"width": 414,"height": 896}, "viewport": {"width": 414,"height": 896}, - "device_scale_factor": 2, "is_mobile": True, "has_touch": True + "device_scale_factor": 2, "has_touch": True }) device_choices.append({ "user_agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:"+self.FIREFOX_VERSION+".0) Gecko/20100101 Firefox/"+self.FIREFOX_VERSION+".0", "screen": {"width": 1920,"height": 1080}, "viewport": {"width": 1920,"height": 1080}, "device_scale_factor": 1, "is_mobile": False, "has_touch": False }) + device_choices.append({ + "user_agent": "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:"+self.FIREFOX_VERSION+".0) Gecko/20100101 Firefox/"+self.FIREFOX_VERSION+".0", + "screen": {"width": 1920,"height": 1080}, "viewport": {"width": 1920,"height": 1080}, + "device_scale_factor": 1, "is_mobile": False, "has_touch": False + }) + device_choices.append({ + "user_agent": "Mozilla/5.0 (X11; Linux; Linux x86_64; rv:"+self.FIREFOX_VERSION+".0) Gecko/20100101 Firefox/"+self.FIREFOX_VERSION+".0", + "screen": {"width": 1920,"height": 1080}, "viewport": {"width": 1920,"height": 1080}, + "device_scale_factor": 1, "is_mobile": False, "has_touch": False + }) device_choices.append({ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:"+self.FIREFOX_VERSION+".0) Gecko/20100101 Firefox/"+self.FIREFOX_VERSION+".0", "screen": {"width": 1366,"height": 768}, "viewport": {"width": 1366,"height": 768}, - "device_scale_factor": 2, "is_mobile": False, "has_touch": False + "device_scale_factor": 1, "is_mobile": False, "has_touch": False }) - + """ return random.choice(device_choices) def get_browser_profile_path(self) -> str: if self.device['user_agent']: if re.search('Mobile', self.device['user_agent']): return '/config/profile_mobile' + elif re.search('Ubuntu', self.device['user_agent']): return '/config/profile_linux_ubuntu' + elif re.search('Fedora', self.device['user_agent']): return '/config/profile_linux_fedora' elif re.search('Linux', self.device['user_agent']): return '/config/profile_linux' elif re.search('Win64', self.device['user_agent']): return '/config/profile_win' return '/config/profile' @@ -430,22 +475,22 @@ def abort_route(self, route: Route) : any(fnmatch.fnmatch(urllib.parse.urlsplit(route.request.url).netloc, pattern) for pattern in good_xfinity_domains): for urls in regex_block_xfinity_domains: if re.search(urls, urllib.parse.urlsplit(route.request.url).hostname + urllib.parse.urlsplit(route.request.url).path): - if SUPPORT: debug_support_logger.debug(f"Blocked URL: {route.request.url}") + if DEBUG_SUPPORT: debug_support_logger.debug(f"Blocked URL: {route.request.url}") #logger.info(f"Blocked URL: {route.request.url}") route.abort('blockedbyclient') return None for urls in regex_good_xfinity_domains: if re.search(urls, urllib.parse.urlsplit(route.request.url).hostname) and \ route.request.resource_type not in bad_resource_types: - if SUPPORT: debug_support_logger.debug(f"Good URL: {route.request.url}") + if DEBUG_SUPPORT: debug_support_logger.debug(f"Good URL: {route.request.url}") #logger.info(f"Good URL: {route.request.url}") route.continue_() return None - if SUPPORT: debug_support_logger.debug(f"Good URL: {route.request.url}") + if DEBUG_SUPPORT: debug_support_logger.debug(f"Good URL: {route.request.url}") route.continue_() return None else: - if SUPPORT: debug_support_logger.debug(f"Blocked URL: {route.request.url}") + if DEBUG_SUPPORT: debug_support_logger.debug(f"Blocked URL: {route.request.url}") route.abort('blockedbyclient') return None @@ -464,7 +509,7 @@ def camelTo_snake_case(self, string: str) -> str: def debug_support(self) -> None: - if SUPPORT and \ + if DEBUG_SUPPORT and \ os.path.exists('/config/'): datetime_format = datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f") @@ -784,13 +829,15 @@ def run(self) -> None: try: self.page.wait_for_url(f'{self.Login_Url}*') expect(self.page).to_have_title('Sign in to Xfinity') + expect(self.page.locator("form[name=\"signin\"]")).to_be_attached() expect(self.page.locator("input#user")).to_be_attached() expect(self.page.locator("input#user")).to_be_editable() except: return None logger.info(f"Entering username (URL: {self.parse_url(self.page.url)})") - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + + self.get_slow_down_login() all_inputs = self.page.get_by_role("textbox").all() if len(all_inputs) != 1: @@ -802,9 +849,9 @@ def run(self) -> None: logger.debug(f"{input.evaluate('el => el.outerHTML')}") self.page.locator("input#user").click() - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + self.get_slow_down_login() self.page.locator("input#user").press_sequentially(self.xfinity_username, delay=150) - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + self.get_slow_down_login() self.debug_support() self.page.locator("input#user").press("Enter") #self.page.locator("button[type=submit]#sign_in").click() @@ -814,6 +861,7 @@ def run(self) -> None: try: self.page.wait_for_url(f'{self.Login_Url}*') expect(self.page).to_have_title('Sign in to Xfinity') + expect(self.page.locator("form[name=\"signin\"]")).to_be_attached() expect(self.page.locator("input#passwd")).to_be_attached() expect(self.page.locator("input#passwd")).to_be_editable() except: @@ -828,9 +876,8 @@ def run(self) -> None: for input in self.page.get_by_role("textbox").all(): logger.debug(f"{input.evaluate('el => el.outerHTML')}") - logger.info(f"Entering password (URL: {self.parse_url(self.page.url)})") - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + self.get_slow_down_login() all_inputs = self.page.get_by_role("textbox").all() if len(all_inputs) != 2: @@ -842,9 +889,11 @@ def run(self) -> None: raise AssertionError("Password form page is missing the user id") self.page.locator("input#passwd").click() - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + self.get_slow_down_login() + + expect(self.page.get_by_label('toggle password visibility')).to_be_visible() self.page.locator("input#passwd").press_sequentially(self.xfinity_password, delay=175) - if self.slow_down_login: time.sleep(random.uniform(.3, .6)) + self.get_slow_down_login() self.debug_support() self.form_signin = self.page.locator("form[name=\"signin\"]").inner_html() for input in self.page.get_by_role("textbox").all(): @@ -862,7 +911,7 @@ def run(self) -> None: logger.info(f"Two Step Verification Check: Page Title {self.page.title()}") logger.info(f"Two Step Verification Check: Page Url {self.page.url}") for input in self.page.get_by_role("textbox").all(): - logger.debug(f"{input.evaluate('el => el.outerHTML')}") + logger.error(f"{input.evaluate('el => el.outerHTML')}") if re.search('id="user"',input.evaluate('el => el.outerHTML')): raise AssertionError("Password form submission failed") @@ -974,12 +1023,23 @@ def run_playwright() -> None: try: run_playwright() - if SUPPORT: exit(99) + if DEBUG_SUPPORT: exit(exit_code.DEBUG_SUPPORT.value) + + # If not HA Addon break and exit with success code + if not HASSIO: break logger.info(f"Sleeping for {int(POLLING_RATE)} seconds") sleep(POLLING_RATE) - except: - mqtt_client.disconnect_mqtt() - exit(98) + + except BaseException as e: + if is_mqtt_available(): + mqtt_client.disconnect_mqtt() + if type(e) == SystemExit : + exit(e.code) + else: + exit(exit_code.MAIN_EXCEPTION.value) + + # Not HA Addon, exit successfully + if not HASSIO: exit(exit_code.SUCCESS.value)