diff --git a/README.md b/README.md index 332d5bd..625464c 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ------ -[![CurrentVersion](https://img.shields.io/badge/Current_Version-v3.1.4-blue.svg)](https://github.com/gcobb321/icloud3_v3) [![Type](https://img.shields.io/badge/Type-Custom_Component-orange.svg)](https://github.com/gcobb321/icloud3_v3) [![HACS](https://img.shields.io/badge/HACS-Custom_Repository-orange.svg)](https://github.com/gcobb321/icloud3_v3) +[![CurrentVersion](https://img.shields.io/badge/Current_Version-v3.1.6-blue.svg)](https://github.com/gcobb321/icloud3_v3) [![Type](https://img.shields.io/badge/Type-Custom_Component-orange.svg)](https://github.com/gcobb321/icloud3_v3) [![HACS](https://img.shields.io/badge/HACS-Custom_Repository-orange.svg)](https://github.com/gcobb321/icloud3_v3) -[![ProjectStage](https://img.shields.io/badge/Project_Stage-Development_Version-forestgreen.svg)](https://github/gcobb321/icloud3_v3) [![Released](https://img.shields.io/badge/Released-December,_2024-forestgreen.svg)](https://github.com/gcobb321/icloud3_v3) +[![ProjectStage](https://img.shields.io/badge/Project_Stage-Development_Version-forestgreen.svg)](https://github/gcobb321/icloud3_v3) [![Released](https://img.shields.io/badge/Released-February, 2025-forestgreen.svg)](https://github.com/gcobb321/icloud3_v3) diff --git a/custom_components/icloud3/Release Notes.txt b/custom_components/icloud3/Release Notes.txt index a31f8c1..2113590 100644 --- a/custom_components/icloud3/Release Notes.txt +++ b/custom_components/icloud3/Release Notes.txt @@ -7,16 +7,23 @@ v3.1.5b1 Removed, Replaced with v3.1.5b2 [here](https://github.com/gcobb321/iclo -### Release Notes - v3.1.5.2 +### Release Notes - v3.1.6 #### 🎉 Improvements and New Features -1. When the password is used to authenticate the Apple Account, Apple sends an email to the account owner that a login/authentication was done. An entry has been added to the Event Log when iCloud3 doe this. The authentication date/time (PST) in the Event Log message is, hopefully, the same as on the email. +1. When the password is used to authenticate the Apple Account, Apple sends an email to the account owner that a login/authentication was done. An entry has been added to the Event Log when iCloud3 doe this. The authentication date/time (PST) in the Event Log message is, hopefully, the same as on the email. View on _Event Log > Show Startup log, Errors & Alerts_. Note: The times the _Event Log_ and email may not match. +2. The Apple Account username is now obscured in the iCloud3 log files. +3. The _Data Sources_ ... screen name was changed to _Apple Accounts & Mobile App_ for clarity. +4. Formatting improvements to the _Apple Accounts & Mobile App screen. +5. The Apple Server Location can now be selected by Apple Account (Applies to China users). +6. Messages are no longer sent to a phone when there is an Internet Connection error. Phones were being hammered with messages when the Internet connection would cycle between an offline and online state. +7. When Pausing all tracking, the time paused and age is now displayed in the Event Log. #### 🐛 Bug Fixes -1. When iCloud3 started and was setting up the tracked devices, if the Mobile App device parameter was set to 'ScanFor' a device and that device was not found in the list of Mobile App devices, an error was aborting the setup process. This resulted in no Apple account or Mobile App devices being set up correctly. This has been fixed. -2. Fixed several problems handling the Mobile App device when 'ScanFor:' was selected for a device on the _Update iCloud3 Device_ screen. -3. Fixed issues dealing with authenticating Apple Accounts in China where the '.cn' url suffix was not being set properly in all cases. - +1. When iCloud3 started and was setting up the tracked devices, if the Mobile App device parameter was set to 'ScanFor a device' and that device was not found in the list of Mobile App devices, an error was aborting the setup process. This resulted in no Apple account or Mobile App devices being set up correctly. This has been fixed. +2. Fixed some problems with accessing Apple Servers in China where the .cn URL suffix was not being appended correctly. +3. Fixed a problem displaying all of the Apple Accounts when multiple Apple Accounts were set up and the first one was not logged into. +#### ☕ Buy me a Coffee +1. Thanks to all that bought me a coffee. I appreciate it. diff --git a/custom_components/icloud3/__init__.py b/custom_components/icloud3/__init__.py index 0e7888d..318b957 100644 --- a/custom_components/icloud3/__init__.py +++ b/custom_components/icloud3/__init__.py @@ -23,11 +23,11 @@ CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN) from .global_variables import GlobalVariables as Gb -from .helpers.messaging import (_evlog, _log, open_ic3log_file_init, post_monitor_msg, +from .helpers.messaging import (_evlog, _log, open_ic3log_file_init, open_ic3log_file, post_monitor_msg, post_evlog_greenbar_msg, post_startup_alert, log_info_msg, log_debug_msg, log_error_msg, log_exception_HA, log_exception) -from .helpers.time_util import (time_now_secs, ) +from .helpers.time_util import (time_now_secs, calculate_time_zone_offset, ) from .helpers.file_io import (async_make_directory, async_directory_exists, async_copy_file, read_json_file, save_json_file, async_rename_file, async_delete_directory, @@ -138,10 +138,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) start_ic3.initialize_directory_filenames() + await Gb.hass.async_add_executor_job( config_file.load_icloud3_configuration_file) + start_ic3.set_log_level(Gb.log_level) await Gb.hass.async_add_executor_job(open_ic3log_file_init) + # new_log_file = Gb.log_debug_flag + # await Gb.hass.async_add_executor_job(open_ic3log_file, new_log_file) + Gb.evlog_btnconfig_url = Gb.conf_profile[CONF_EVLOG_BTNCONFIG_URL].strip() Gb.evlog_version = Gb.conf_profile['event_log_version'] Gb.EvLog = event_log.EventLog(Gb.hass) @@ -173,12 +178,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Gb.EvLog.display_user_message(f"Starting {ICLOUD3_VERSION_MSG}") + calculate_time_zone_offset() Gb.PyiCloudValidateAppleAcct = PyiCloudValidateAppleAcct() Gb.username_valid_by_username = {} if Gb.use_data_source_ICLOUD: # v3.0 --> v3.1 file location change - await Gb.hass.async_add_executor_job(move_icloud_cookies_to_icloud3_apple_acct) - await Gb.hass.async_add_executor_job(pyicloud_ic3_interface.check_all_apple_accts_valid_upw) + if Gb.conf_tracking['setup_icloud_session_early']: + await Gb.hass.async_add_executor_job(move_icloud_cookies_to_icloud3_apple_acct) + await Gb.hass.async_add_executor_job(pyicloud_ic3_interface.check_all_apple_accts_valid_upw) # set_up_default_area_id() diff --git a/custom_components/icloud3/config_flow.py b/custom_components/icloud3/config_flow.py index 73dacc2..922ffb9 100644 --- a/custom_components/icloud3/config_flow.py +++ b/custom_components/icloud3/config_flow.py @@ -31,6 +31,7 @@ CONF_APPLE_ACCOUNTS, CONF_APPLE_ACCOUNT, CONF_TOTP_KEY, CONF_USERNAME, CONF_PASSWORD, CONF_DEVICES, CONF_SETUP_ICLOUD_SESSION_EARLY, CONF_DATA_SOURCE, CONF_VERIFICATION_CODE, CONF_LOCATE_ALL, + CONF_SERVER_LOCATION, CONF_SERVER_LOCATION_NEEDED, CONF_TRACK_FROM_ZONES, CONF_TRACK_FROM_BASE_ZONE_USED, CONF_TRACK_FROM_BASE_ZONE, CONF_TRACK_FROM_HOME_ZONE, CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, CONF_LOG_ZONES, @@ -444,7 +445,7 @@ def initialize_options(self): self.aa_page_no = 0 # apple_accts List form page number, starting with 0 self.conf_apple_acct = {} # apple acct item selected self.apple_acct_items_by_username = {} # Selection list for the apple accounts on data_sources and reauth screens - self.aa_idx = 0 + self.aa_idx = 0 self.apple_acct_reauth_username = '' self.add_apple_acct_flag = False @@ -532,7 +533,10 @@ def _initialize_self_PyiCloud_fields_from_Gb(self): Gb.conf_tracking[CONF_DATA_SOURCE] = Gb.conf_tracking[CONF_DATA_SOURCE].replace('mobapp', MOBAPP) self.data_source = Gb.conf_tracking[CONF_DATA_SOURCE] - self.icloud_server_suffix = Gb.icloud_server_suffix + if self.PyiCloud: + self.apple_server_location = self.PyiCloud.apple_server_location + else: + self.apple_server_location = 'usa' #------------------------------------------------------------------------------------------- def _set_initial_icloud3_device_tracker_area_id(self): @@ -577,10 +581,7 @@ async def async_step_menu(self, user_input=None, errors=None): Gb.trace_prefix = 'CONFIG' Gb.config_flow_flag = True - if (self.username != '' and self.password != '' - and instr(Gb.conf_tracking[CONF_DATA_SOURCE], ICLOUD) is False): - self.header_msg = 'icloud_acct_data_source_warning' - elif Gb.internet_connection_error: + if Gb.internet_connection_error: self.header_msg = 'internet_connection_err' elif self.PyiCloud is None and self.username: self.header_msg = 'icloud_acct_not_logged_into' @@ -1954,7 +1955,6 @@ async def async_step_data_source( self, user_input=None, errors=None, Updata Data Sources form enables/disables finddev and mobile app datasources and adds/updates/removes an Apple account using the Update Username/Password screen ''' - self.step_id = 'data_source' self.errors = errors or {} self.errors_user_input = {} @@ -1963,14 +1963,6 @@ async def async_step_data_source( self, user_input=None, errors=None, self.actions_list_default = '' action_item = '' - # if (Gb.internet_connection_error - # and Gb.PyiCloudValidateAppleAcct.is_internet_available - # and Gb.internet_connection_test is False): - # reset_internet_connection_error() - # Gb.internet_connection_error = False - # Gb.internet_connection_error_secs = 0 - # list_add(self.config_parms_update_control, 'restart') - if Gb.internet_connection_error: self.errors['base'] = 'internet_connection_err' @@ -1978,6 +1970,9 @@ async def async_step_data_source( self, user_input=None, errors=None, user_input, action_item = self._action_text_to_item(user_input) if self._is_apple_acct_setup() is False: self.errors['apple_accts'] = 'icloud_acct_not_set_up' + elif (isnot_empty(Gb.conf_apple_accounts) + and instr(Gb.conf_tracking[CONF_DATA_SOURCE], ICLOUD) is False): + self.errors['apple_accts'] = 'icloud_acct_data_source_warning' if user_input is None: self.actions_list_default = 'update_apple_acct' @@ -1985,6 +1980,7 @@ async def async_step_data_source( self, user_input=None, errors=None, data_schema=form_data_source(self), errors=self.errors) + user_input = self._update_data_source(user_input) self.log_step_info(user_input, action_item) if action_item == 'cancel_goto_menu': @@ -2044,23 +2040,22 @@ async def async_step_data_source( self, user_input=None, errors=None, f"UserInput-{log_user_input}, Errors-{errors}") if action_item == 'update_apple_acct': - self._update_data_source(user_input) self.aa_page_item[self.aa_page_no] = self.conf_apple_acct[CONF_USERNAME] return await self.async_step_update_apple_acct() if action_item == 'verification_code': return await self.async_step_reauth(called_from_step_id='data_source') - if user_input[CONF_DATA_SOURCE] == ',': + if user_input[CONF_DATA_SOURCE] == '': self.errors['base'] = 'icloud_acct_no_data_source' if self.errors == {}: if action_item == 'add_change_apple_acct': action_item == 'save' - user_input[CONF_DATA_SOURCE] = list_add(user_input[CONF_DATA_SOURCE], ICLOUD) if action_item == 'save': - self._update_data_source(user_input) + if self.data_source != Gb.conf_tracking[CONF_DATA_SOURCE]: + self._update_config_file_tracking(user_input) return await self.async_step_menu() @@ -2072,10 +2067,24 @@ async def async_step_data_source( self, user_input=None, errors=None, #........................................................................................ def _update_data_source(self, user_input): - self.data_source = list_to_str(user_input[CONF_DATA_SOURCE], ',') - user_input[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] = self.icloud_server_suffix + self.data_source = [] + ds_apple_acct = user_input.pop('data_source_apple_acct', []) + ds_mobapp = user_input.pop('data_source_mobapp', []) + + data_source = [] + if isnot_empty(ds_apple_acct): + list_add(data_source, ICLOUD) + if isnot_empty(ds_mobapp): + list_add(data_source, MOBAPP) + + self.data_source = list_to_str(data_source, ',') user_input[CONF_DATA_SOURCE] = self.data_source - self._update_config_file_tracking(user_input) + + if self.data_source != Gb.conf_tracking[CONF_DATA_SOURCE]: + self._update_config_file_tracking(user_input) + + return user_input + #........................................................................................ def _is_apple_acct_setup(self): @@ -2101,14 +2110,6 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): action_item = '' await self._async_write_icloud3_configuration_file() - # if (Gb.internet_connection_error - # and Gb.PyiCloudValidateAppleAcct.is_internet_available - # and Gb.internet_connection_test is False): - # reset_internet_connection_error() - # Gb.internet_connection_error = False - # Gb.internet_connection_error_secs = 0 - # list_add(self.config_parms_update_control, 'restart') - if Gb.internet_connection_error: self.errors['base'] = 'internet_connection_err' @@ -2121,6 +2122,10 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): user_input = self._option_text_to_parm(user_input, 'account_selected', self.apple_acct_items_by_username) user_input = self._strip_spaces(user_input, [CONF_USERNAME, CONF_PASSWORD, CONF_TOTP_KEY]) + if CONF_SERVER_LOCATION in user_input: + user_input = self._option_text_to_parm(user_input, CONF_SERVER_LOCATION, APPLE_SERVER_LOCATION_OPTIONS) + else: + user_input[CONF_SERVER_LOCATION] = 'usa' if (user_input[CONF_LOCATE_ALL] is False and self._can_disable_locate_all(user_input) is False): @@ -2131,23 +2136,24 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): user_input=user_input, errors=self.errors) - user_input[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] = 'cn' \ - if user_input.get('url_suffix_china') is True else '' - user_input[CONF_USERNAME] = user_input[CONF_USERNAME].lower() user_input[CONF_TOTP_KEY] = '' #user_input[CONF_TOTP_KEY].upper() ui_username = user_input[CONF_USERNAME] ui_password = user_input[CONF_PASSWORD] - ui_apple_acct ={CONF_USERNAME: ui_username, - CONF_PASSWORD: ui_password, - CONF_TOTP_KEY: user_input[CONF_TOTP_KEY], - CONF_LOCATE_ALL: user_input[CONF_LOCATE_ALL]} + + # Make an apple acct dict to compare to the current one + ui_apple_acct = DEFAULT_APPLE_ACCOUNT_CONF.copy() + ui_apple_acct[CONF_USERNAME] = ui_username + ui_apple_acct[CONF_PASSWORD] = ui_password + ui_apple_acct[CONF_TOTP_KEY] = user_input[CONF_TOTP_KEY] + ui_apple_acct[CONF_LOCATE_ALL] = user_input[CONF_LOCATE_ALL] + ui_apple_acct[CONF_SERVER_LOCATION] = user_input[CONF_SERVER_LOCATION] conf_username = self.conf_apple_acct[CONF_USERNAME] conf_password = decode_password(self.conf_apple_acct[CONF_PASSWORD]) conf_locate_all = self.conf_apple_acct[CONF_LOCATE_ALL] - add_log_file_filter(ui_username, hide_text=True) + add_log_file_filter(ui_username, f"**{self.aa_idx}**") add_log_file_filter(ui_password) if Gb.log_debug_flag: @@ -2185,11 +2191,10 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): self.errors[CONF_USERNAME] = 'icloud_acct_username_inuse_error' action_item = '' - # Saving an existing account with same password, no change, nothing to do + # Saving an existing account with no changes, nothing to do if (action_item == 'save_log_into_apple_acct' and ui_apple_acct == self.conf_apple_acct - and user_input[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] == \ - Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] + # and user_input[CONF_SERVER_LOCATION] == self.conf_apple_acct[CONF_SERVER_LOCATION] and user_input[CONF_LOCATE_ALL] != conf_locate_all and Gb.PyiCloud_by_username.get(ui_username) is not None): self.header_msg = 'icloud_acct_logged_into' @@ -2209,7 +2214,7 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): return await self.async_step_delete_apple_acct(user_input=user_input) - username_password_valid = True + username_password_valid = True aa_login_info_changed = False other_flds_changed = False @@ -2217,8 +2222,7 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): # Apple acct login info changed, validate it without logging in if (conf_username != user_input[CONF_USERNAME] or conf_password != user_input[CONF_PASSWORD] - or user_input[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] != \ - Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] + or user_input[CONF_SERVER_LOCATION] != self.conf_apple_acct[CONF_SERVER_LOCATION] or ui_username not in Gb.PyiCloud_by_username or Gb.PyiCloud_by_username.get(ui_username) is None): aa_login_info_changed = True @@ -2245,11 +2249,15 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): user_input=user_input, errors=self.errors) - if aa_login_info_changed or other_flds_changed: - self._update_conf_apple_accounts(self.aa_idx, user_input) - await self._async_write_icloud3_configuration_file() + # If nothing changed, the last login may have failed and the config was set back to what it + # was. The Gb.PyiCloud_by_username will be the PyiCloudLoggedInto that failed, not the + # correct one. Reset it to the correct PyiCloud + if (aa_login_info_changed is False + and self.PyiCloud + and self.PyiCloud.username == ui_username): + Gb.PyiCloud_by_username[ui_username] = self.PyiCloud - # Log into the account + # A new config, Log into the account if (aa_login_info_changed or ui_username not in Gb.PyiCloud_by_username or Gb.PyiCloud_by_username.get(ui_username) is None): @@ -2258,19 +2266,23 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): user_input, called_from_step_id='update_apple_acct') + if successful_login: if instr(self.data_source, ICLOUD) is False: self._update_data_source({CONF_DATA_SOURCE: [ICLOUD, self.data_source]}) Gb.PyiCloud_by_username[user_input[CONF_USERNAME]] = \ self.PyiCloud or Gb.PyiCloudLoggingInto - Gb.PyiCloud_password_by_username[user_input[CONF_USERNAME]] = \ - user_input[CONF_PASSWORD] + Gb.PyiCloud_password_by_username[user_input[CONF_USERNAME]] = user_input[CONF_PASSWORD] apple_acct = user_input[CONF_USERNAME] if (aa_login_info_changed and ui_username in Gb.username_pyicloud_503_connection_error): self.errors['base'] = 'icloud_acct_updated_not_logged_into' + if aa_login_info_changed or other_flds_changed: + self._update_conf_apple_accounts(self.aa_idx, user_input) + await self._async_write_icloud3_configuration_file() + if self.PyiCloud.requires_2fa: action_item = 'verification_code' else: @@ -2279,6 +2291,7 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): if action_item == 'verification_code': return await self.async_step_reauth(called_from_step_id='update_apple_acct') + self._build_apple_accounts_list() return self.async_show_form(step_id='update_apple_acct', data_schema=form_update_apple_acct(self), errors=self.errors) @@ -2321,6 +2334,7 @@ def _update_conf_apple_accounts(self, aa_idx, user_input, remove_acct_flag=False self.conf_apple_acct[CONF_PASSWORD] = encode_password(user_input[CONF_PASSWORD]) self.conf_apple_acct[CONF_TOTP_KEY] = user_input[CONF_TOTP_KEY] self.conf_apple_acct[CONF_LOCATE_ALL] = user_input[CONF_LOCATE_ALL] + self.conf_apple_acct[CONF_SERVER_LOCATION] = user_input[CONF_SERVER_LOCATION] # Delete an existing account if remove_acct_flag: @@ -2562,7 +2576,7 @@ async def async_step_reauth(self, user_input=None, errors=None, if Gb.internet_connection_error: self.errors['base'] = 'internet_connection_err' elif self.PyiCloud and self.PyiCloud.requires_2fa: - self.errors['base'] ='verification_code_needed' + self.errors['base'] = 'verification_code_needed' log_debug_msg( f"OF-{self.step_id.upper()} ({action_item}) > " f"FromForm-{called_from_step_id}, UserInput-{user_input}, Errors-{errors}") @@ -2633,9 +2647,14 @@ async def async_step_reauth(self, user_input=None, errors=None, errors=self.errors) elif action_item == 'request_verification_code': + self.errors['base'] = 'verification_code_requested' + post_event( f"{EVLOG_NOTICE}Configure Apple Acct > {self.PyiCloud.account_owner}, " + f"Requested a new Verification Code") + reauth_username = ui_username await self.async_pyicloud_reset_session(username, password) + return self.async_show_form(step_id='reauth', data_schema=form_reauth(self, reauth_username=reauth_username), errors=self.errors) @@ -2741,7 +2760,7 @@ async def async_pyicloud_reset_session(self, username, password): if PyiCloud: post_event( f"{EVLOG_NOTICE}Configure Apple Acct > {PyiCloud.account_owner}, " - f"Waiting for 6-digit Verification Code to be entered") + f"Waiting for the 6-digit Verification Code to be entered") return except PyiCloudFailedLoginException as err: @@ -3172,7 +3191,6 @@ def _set_other_device_field_values(self, user_input): user_input[CONF_DEVICE_TYPE] = device_type inzone_interval_item = NO_MOBAPP if user_input[CONF_MOBILE_APP_DEVICE] == 'None' else device_type user_input[CONF_INZONE_INTERVAL] = DEFAULT_GENERAL_CONF[CONF_INZONE_INTERVALS][inzone_interval_item] - return user_input #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> @@ -3820,15 +3838,20 @@ def _build_apple_accounts_list(self): elif username == '': continue else: + valid_upw = Gb.username_valid_by_username.get(username) PyiCloud = Gb.PyiCloud_by_username.get(username) - if PyiCloud is None or PyiCloud.is_DeviceSvc_setup_complete is False: + if PyiCloud is None or PyiCloud.is_DeviceSvc_setup_complete is False: aa_text = f"{username}{RARROW}{RED_ALERT}" - if Gb.username_valid_by_username.get(username): + if valid_upw is False: + aa_text += 'NOT LOGGED IN, INVALID USERNAME/PASSWORD' + elif instr(Gb.conf_tracking[CONF_DATA_SOURCE], ICLOUD) is False: + aa_text += 'NOT LOGGED IN, APPLE DATA SOURCE DISABLED' + elif valid_upw is None: aa_text += 'NOT LOGGED INTO THIS APPLE ACCOUNT' else: - aa_text += 'NOT LOGGED IN, INVALID USERNAME/PASSWORD' + aa_text += 'NOT LOGGED IN DUE TO ANOTHER ERROR, SEE EVENT LOG' self.apple_acct_items_by_username[username] = aa_text - return + continue aa_text = f"{PyiCloud.account_owner_username.split('@')[0]}{RARROW}" if PyiCloud.requires_2fa: @@ -4486,13 +4509,10 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): username = user_input[CONF_USERNAME].lower() password = user_input[CONF_PASSWORD] - add_log_file_filter(username, hide_text=True) + add_log_file_filter(username, f"**{self.aa_idx}**") add_log_file_filter(password) - icloud_server_suffix = user_input.get(CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, - Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX]) - - start_ic3.setup_icloud_server_url(icloud_server_suffix) + apple_server_location = user_input.get(CONF_SERVER_LOCATION,'usa') log_info_msg( f"Apple Acct > {username}, Logging in, " f"UserInput-{user_input}, Errors-{self.errors}, " @@ -4502,7 +4522,7 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): PyiCloud = Gb.PyiCloud_by_username.get(username) if (PyiCloud and password == PyiCloud.password - and icloud_server_suffix == PyiCloud.icloud_server_suffix + and apple_server_location == PyiCloud.apple_server_location and PyiCloud.login_successful): self.PyiCloud = PyiCloud self.username = username @@ -4520,7 +4540,8 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): return False event_msg =(f"{EVLOG_NOTICE}Configure Settings > Logging into Apple Account {username}") - if icloud_server_suffix != '': event_msg += f", iCloud.com ServerSuffix-{icloud_server_suffix}" + if apple_server_location != 'usa': + event_msg += f", iCloud.com ServerSuffix-{apple_server_location}" log_info_msg(event_msg) try: @@ -4531,14 +4552,13 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): self.create_PyiCloudService_config_flow, username, password, - icloud_server_suffix) + apple_server_location) # Successful login, set PyiCloud fields self.PyiCloud = PyiCloud self.username = username self.password = password - self.icloud_server_suffix = icloud_server_suffix - Gb.icloud_server_suffix = icloud_server_suffix + self.apple_server_location = apple_server_location Gb.username_valid_by_username[username] = True log_info_msg(f"Apple Acct > {username}, Login successful, {self.PyiCloud}") @@ -4564,6 +4584,7 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): data_schema=self.form_schema(called_from_step_id), errors=self.errors) + # Login Failed, display error messages except (PyiCloudFailedLoginException) as err: err = str(err) _CF_LOGGER.error(f"Error logging into Apple Acct: {err}") @@ -4578,12 +4599,12 @@ async def log_into_apple_account(self, user_input, called_from_step_id=None): if Gb.PyiCloudLoggingInto is not None: country_code = Gb.PyiCloudLoggingInto.account_country_code - icloud_server_suffix = Gb.PyiCloudLoggingInto.icloud_server_suffix + apple_server_location = Gb.PyiCloudLoggingInto.apple_server_location - if (country_code == 'CHN' and icloud_server_suffix == ''): - error_msg += '_cn' - elif (country_code != 'CHN' and icloud_server_suffix == 'cn'): - error_msg += '_xcn' + if (country_code == 'CHN' and apple_server_location == 'usa'): + self.errors[CONF_SERVER_LOCATION] = 'icloud_acct_login_error_302_cn' + elif (country_code != 'CHN' and apple_server_location == '.cn'): + self.errors[CONF_SERVER_LOCATION] = 'icloud_acct_login_error_302_usa' elif response_code == 400: error_msg = 'icloud_acct_login_error_user_pw' @@ -4628,7 +4649,7 @@ async def _async_validate_username_password(username, password): return valid_apple_acct #-------------------------------------------------------------------- - def create_PyiCloudService_config_flow(self, username, password, icloud_server_suffix): + def create_PyiCloudService_config_flow(self, username, password, apple_server_location): ''' Create the PyiCloudService object without going through the error checking and authentication test routines. This is used by config_flow to open a second @@ -4636,9 +4657,9 @@ def create_PyiCloudService_config_flow(self, username, password, icloud_server_s ''' PyiCloud = PyiCloudService( username, password, + apple_server_location=apple_server_location, cookie_directory=Gb.icloud_cookie_directory, session_directory=Gb.icloud_session_directory, - icloud_server_suffix=icloud_server_suffix, config_flow_login=True) if PyiCloud and PyiCloud.login_successful: @@ -5205,6 +5226,9 @@ def _option_parm_to_text(self, pname, option_list_key_text, conf_device=False): elif pname in Gb.conf_tracking: pvalue_key = Gb.conf_tracking[pname] + elif pname in self.conf_apple_acct: + pvalue_key = self.conf_apple_acct[pname] + elif pname in Gb.conf_general and pname in self.conf_device: if conf_device: pvalue_key = self.conf_device[pname] diff --git a/custom_components/icloud3/config_flow_forms.py b/custom_components/icloud3/config_flow_forms.py index eaf0094..2085af5 100644 --- a/custom_components/icloud3/config_flow_forms.py +++ b/custom_components/icloud3/config_flow_forms.py @@ -3,7 +3,7 @@ area_registry as ar,) import homeassistant.helpers.config_validation as cv import voluptuous as vol -import pyotp +# import pyotp import time from datetime import datetime @@ -16,6 +16,7 @@ CONF_EVLOG_CARD_DIRECTORY, CONF_EVLOG_BTNCONFIG_URL, CONF_APPLE_ACCOUNT, CONF_USERNAME, CONF_PASSWORD, CONF_LOCATE_ALL, CONF_TOTP_KEY, CONF_DATA_SOURCE, CONF_VERIFICATION_CODE, + CONF_SERVER_LOCATION, CONF_SERVER_LOCATION_NEEDED, CONF_TRACK_FROM_ZONES, CONF_LOG_ZONES, CONF_TRACK_FROM_BASE_ZONE_USED, CONF_TRACK_FROM_BASE_ZONE, CONF_TRACK_FROM_HOME_ZONE, CONF_PICTURE, CONF_ICON, CONF_DEVICE_TYPE, CONF_INZONE_INTERVALS, @@ -226,12 +227,13 @@ def form_data_source(self): self.actions_list = [] self.actions_list.extend(APPLE_ACCOUNT_ACTIONS) self.actions_list.extend(ACTION_LIST_ITEMS_BASE) - if self.username != '' and self.password != '' and instr(self.data_source, ICLOUD) is False: - self.errors['base'] = 'icloud_acct_data_source_warning' default_action = self.actions_list_default if self.actions_list_default else 'save' self.actions_list_default = '' + mobile_app_used_default = [MOBILE_APP_USED_HEADER] if instr(Gb.conf_tracking[CONF_DATA_SOURCE], MOBAPP) else [] + apple_acct_used_default = [APPLE_ACCT_USED_HEADER] if instr(Gb.conf_tracking[CONF_DATA_SOURCE], ICLOUD) else [] + # Build list of all apple accts self.apple_acct_items_list= [apple_acct_item for apple_acct_username, apple_acct_item in self.apple_acct_items_by_username.items() @@ -248,15 +250,22 @@ def form_data_source(self): if default_item not in self.apple_acct_items_displayed: default_item = self.apple_acct_items_displayed[0] - if is_empty(Gb.devicenames_x_mobapp_dnames): - mobapp_interface.get_mobile_app_integration_device_info() - if is_empty(Gb.devicenames_x_mobapp_dnames): - self.errors['data_source_mobapp'] = 'mobile_app_error' + if instr(Gb.conf_tracking[CONF_DATA_SOURCE], MOBAPP): + if is_empty(Gb.devicenames_x_mobapp_dnames): + mobapp_interface.get_mobile_app_integration_device_info() + # if is_empty(Gb.devicenames_x_mobapp_dnames): + # self.errors['data_source_mobapp'] = 'mobile_app_error' return vol.Schema({ - vol.Optional('data_source', - default=Gb.conf_tracking[CONF_DATA_SOURCE].replace(' ', '').split(',')): - cv.multi_select(DATA_SOURCE_OPTIONS), + vol.Optional('data_source_mobapp', + default=mobile_app_used_default): + cv.multi_select([MOBILE_APP_USED_HEADER]), + # vol.Optional('data_source', + # default=Gb.conf_tracking[CONF_DATA_SOURCE].replace(' ', '').split(',')): + # cv.multi_select(DATA_SOURCE_OPTIONS), + vol.Optional('data_source_apple_acct', + default=apple_acct_used_default): + cv.multi_select([APPLE_ACCT_USED_HEADER]), vol.Optional('apple_accts', default=default_item): selector.SelectSelector(selector.SelectSelectorConfig( @@ -307,8 +316,6 @@ def form_update_apple_acct(self): else: password_selector = selector.TextSelector(selector.TextSelectorConfig(type='password')) - url_suffix_china = (Gb.icloud_server_suffix == 'cn') - if (self.add_apple_acct_flag is False and username not in Gb.PyiCloud_by_username): self.errors['base'] = 'icloud_acct_not_logged_into' @@ -333,19 +340,21 @@ def form_update_apple_acct(self): # vol.Optional(CONF_TOTP_KEY, # default='For future use in supporting hardware keys (YubiKey)'): # selector.TextSelector(), - vol.Optional('locate_all', - default=locate_all): - cv.boolean, + }) - if self.aa_idx == 0: + if Gb.country_code in ['cn', 'hk'] or Gb.conf_tracking[CONF_SERVER_LOCATION_NEEDED]: schema.update({ - vol.Optional('url_suffix_china', - default=url_suffix_china): - cv.boolean, + vol.Required(CONF_SERVER_LOCATION, + default=self._option_parm_to_text(CONF_SERVER_LOCATION, APPLE_SERVER_LOCATION_OPTIONS)): + selector.SelectSelector(selector.SelectSelectorConfig( + options=dict_value_to_list(APPLE_SERVER_LOCATION_OPTIONS), mode='dropdown')), }) schema.update({ + vol.Optional('locate_all', + default=locate_all): + cv.boolean, vol.Required('action_items', default=self.action_default_text('save_log_into_apple_acct')): selector.SelectSelector(selector.SelectSelectorConfig( @@ -933,6 +942,10 @@ def form_format_settings(self): default=Gb.conf_general[CONF_DISPLAY_GPS_LAT_LONG]): # cv.boolean, selector.BooleanSelector(), + vol.Required(CONF_SERVER_LOCATION_NEEDED, + default=Gb.conf_tracking[CONF_SERVER_LOCATION_NEEDED]): + # cv.boolean, + selector.BooleanSelector(), vol.Required('evlog_header', default=IC3_DIRECTORY_HEADER): diff --git a/custom_components/icloud3/const.py b/custom_components/icloud3/const.py index efd828a..c69e1d4 100644 --- a/custom_components/icloud3/const.py +++ b/custom_components/icloud3/const.py @@ -12,8 +12,8 @@ # from homeassistant.const import (Platform) -VERSION = '3.1.5.2' -VERSION_BETA = 'b2' +VERSION = '3.1.6' +VERSION_BETA = '' #----------------------------------------- DOMAIN = 'icloud3' PLATFORMS = ['sensor', 'device_tracker'] @@ -127,7 +127,7 @@ # Apple is using a country specific iCloud server based on the country code in pyicloud_ic3. # Add to the HOME_ENDPOINT & SETUP_ENDPOINT urls if the HA country code is one of these values. ICLOUD_SERVER_COUNTRY_CODE = ['cn', 'CN'] -ICLOUD_SERVER_ENDPOINT = { +APPLE_SERVER_ENDPOINT = { 'home': 'https://www.icloud.com', 'setup': 'https://setup.icloud.com/setup/ws/1', 'auth': 'https://idmsa.apple.com/appleauth/auth', @@ -205,10 +205,10 @@ def DEVICE_TYPE_FNAME(device_type): EVENT_LOG_CLEAR_SECS = 900 # Clear event log data interval EVENT_LOG_CLEAR_CNT = 50 # Number of recds to display when clearing event log -EVLOG_BTN_URLS = {'btnConfig': '', - 'btnBuyMeACoffee': '', - 'btnIssues': '', - 'btnHelp': ''} +EVLOG_BTN_URLS = {'btnConfig': '', + 'btnBuyMeACoffee': '', + 'btnIssues': '', + 'btnHelp': ''} #Devicename config parameter file extraction DI_DEVICENAME = 0 @@ -665,6 +665,8 @@ def DEVICE_TYPE_FNAME(device_type): CONF_DATA_SOURCE = 'data_source' CONF_VERIFICATION_CODE = 'verification_code' CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX = 'icloud_server_endpoint_suffix' +CONF_SERVER_LOCATION = 'server_location' +CONF_SERVER_LOCATION_NEEDED = 'apple_server_location_needed' CONF_ENCODE_PASSWORD = 'encode_password' CONF_SETUP_ICLOUD_SESSION_EARLY = 'setup_icloud_session_early' CONF_DEVICENAME = 'device_name' @@ -873,6 +875,7 @@ def DEVICE_TYPE_FNAME(device_type): CONF_PASSWORD: '', CONF_ENCODE_PASSWORD: True, CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX: '', + CONF_SERVER_LOCATION_NEEDED: False, CONF_SETUP_ICLOUD_SESSION_EARLY: True, CONF_DATA_SOURCE: f'{ICLOUD},{MOBAPP}', CONF_APPLE_ACCOUNTS: [], @@ -884,6 +887,7 @@ def DEVICE_TYPE_FNAME(device_type): CONF_PASSWORD: '', CONF_TOTP_KEY: '', CONF_LOCATE_ALL: True, + CONF_SERVER_LOCATION: 'usa', } DEFAULT_DEVICE_CONF = { diff --git a/custom_components/icloud3/const_config_flow.py b/custom_components/icloud3/const_config_flow.py index 1131005..86a9501 100644 --- a/custom_components/icloud3/const_config_flow.py +++ b/custom_components/icloud3/const_config_flow.py @@ -9,7 +9,7 @@ 'Parameters Menu' ] MENU_KEY_TEXT = { - 'data_source': 'DATA SOURCES > APPLE ACCOUNT & MOBILE APP > Select Location Data Sources, Apple Account Username/Password', + 'data_source': 'APPLE ACCOUNTS & MOBILE APP > Add, Change and Delete Apple Accounts, Enable Monitoring the Mobile App ', 'device_list': 'ICLOUD3 DEVICES > Add, Change and Delete Tracked and Monitored Devices', 'verification_code': 'ENTER/REQUEST AN APPLE ACCOUNT VERIFICATION CODE > Enter or Request the 6-digit Apple Account Verification Code', 'away_time_zone': 'AWAY TIME ZONE > Select the displayed time zone for devices away from Home', @@ -18,7 +18,7 @@ 'tools': 'TOOLS > Log Level, Delete Apple Acct & Device Assignment, Delete Apple Acct Cookie & iCloud3 Config files, Repair sensor ‘_2’ entity name errors, Restart HA/Reload iCloud3', 'tracking_parameters': 'TRACKING PARAMETERS > Nearby Device Info, Accuracy Thresholds & Other Location Request Intervals', - 'format_settings': 'FIELD FORMATS & EVENT LOG CONGUG OVERRIDES > Zone Display & Device Tracker State format, Unit of Measure/Time & Distance format, Picture Dir Filters', + 'format_settings': 'FIELD FORMATS & OTHER PARAMETERS > Zone Display & Device Tracker State formats, Unit of Measure/Time & Distance formats, Picture Dir Filters, Event Log Overrides, etc', 'display_text_as': 'DISPLAY TEXT AS > Event Log Text Replacement', 'waze': 'WAZE ROUTE DISTANCE, TIME & HISTORY > Route Server and Parameters, Waze History Database Parameters and Controls', 'special_zones': 'SPECIAL ZONES > Enter Zone Delay Time. Stationary Zone. Primary Track-from-Home Zone Override', @@ -207,15 +207,17 @@ 'iCloud': 'APPLE ACCOUNT - Location data is provided for devices in the Family Sharing List', 'MobApp': 'HA MOBILE APP - Location data and zone enter/exit triggers from devices with the Mobile App' } + +# Apple Server Endpoint value - Add onto the Server URL in PyiCloud_ic3 if this starts with a period ('.') +APPLE_SERVER_LOCATION_OPTIONS = { + 'usa': 'USA/OTHER - The Apple Server does not require a URL Endpoint suffix', + '.cn': 'CHINA - Add `.cn` to the Apple Server URL Endpoint' + } DELETE_APPLE_ACCT_DEVICE_ACTION_OPTIONS = { 'reassign_devices': 'REASSIGN DEVICES > Search for another Apple Account with this device device and reassign it to that Apple Account. Set it to Inactive if one is not found', 'delete_devices': 'DELETE DEVICES > Delete all devices that are using this Apple Account', 'set_devices_inactive': 'SET DEVICES TO INACTIVE > Set the devices using this Apple Account to Inactive. They will be assigned to another Apple Account later' } -ICLOUD_SERVER_ENDPOINT_SUFFIX_OPTIONS = { - 'none': 'Use normal Apple iCloud Servers', - 'cn': 'China - Use Apple iCloud Servers located in China' - } MOBAPP_DEVICE_NONE_OPTIONS = {'None': 'None - The Mobile App is not installed on this device'} PICTURE_NONE_KEY_TEXT = {'None': 'None - Display the Device’s Icon instead of a picture'} LOG_ZONES_KEY_TEXT = { @@ -382,6 +384,10 @@ "restart_ha": "RESTART HA, RELOAD ICLOUD3 > Restart HA or Reload iCloud3", "return": "MAIN MENU > Return to the Main Menu" } + +# Section Headers used on various forms +MOBILE_APP_USED_HEADER = 'Monitor the Mobile App Integration devices location data and zone enter/exit triggers' +APPLE_ACCT_USED_HEADER = 'Request location data from the devices in the Apple Account`s Family Sharing List' RARELY_UPDATED_PARMS = 'rarely_updated_parms' RARELY_UPDATED_PARMS_HEADER = ("➤ RARELY USED PARAMETERS - Display inZone & Fixed Interval, Track-from-Zone and Track-from-Home Zone Override parameters the parameters") WAZE_USED_HEADER = ("The Waze Route Service provides the travel time and distance information from your " diff --git a/custom_components/icloud3/global_variables.py b/custom_components/icloud3/global_variables.py index a8470e3..039d2ab 100644 --- a/custom_components/icloud3/global_variables.py +++ b/custom_components/icloud3/global_variables.py @@ -25,7 +25,7 @@ from .const import (DEVICENAME_MOBAPP, VERSION, VERSION_BETA, NOT_SET, HOME_FNAME, HOME, STORAGE_DIR, WAZE_USED, - ICLOUD_SERVER_ENDPOINT, + APPLE_SERVER_ENDPOINT, DEFAULT_GENERAL_CONF, CONF_UNIT_OF_MEASUREMENT, CONF_DISPLAY_ZONE_FORMAT, CONF_DEVICE_TRACKER_STATE_SOURCE, @@ -106,7 +106,7 @@ class GlobalVariables(object): PyiCloudSession_by_username = {} # Session object for a username, set in Session so exists on an error username_pyicloud_503_connection_error = [] # Session object for a username, set in Session so exists on an error - log_file_filter_items = [] # items to be filtered from the log file (passwords, etc) + log_file_filter_items = {} # items to be filtered from the log file (passwords, etc) log_file_hide_items = [] # email extensions filter from apple accounts to remove in the icloud3-0.log file (messaging.py) disable_log_filter = False @@ -145,9 +145,9 @@ class GlobalVariables(object): # icloud.com url suffix for china HOME_ENDPOINT & SETUP_ENDPOINT .com --> .com.cn for China icloud_server_suffix = '' - HOME_ENDPOINT = ICLOUD_SERVER_ENDPOINT['home'] - SETUP_ENDPOINT = ICLOUD_SERVER_ENDPOINT['setup'] - AUTH_ENDPOINT = ICLOUD_SERVER_ENDPOINT['auth'] + HOME_ENDPOINT = APPLE_SERVER_ENDPOINT['home'] + SETUP_ENDPOINT = APPLE_SERVER_ENDPOINT['setup'] + AUTH_ENDPOINT = APPLE_SERVER_ENDPOINT['auth'] # Global Object Dictionaries @@ -170,6 +170,8 @@ class GlobalVariables(object): internet_connection_test = False # Raise ConnectionError in PyiCloud_session, set in service_handler - Show/Hide Tracking Monitors internet_connection_error = False # Set in PyiCloud_session from an http connection error. Shuts down all PyiCloud requests internet_connection_error_secs = 0 # Time https connection error received + internet_connection_error_code = 0 # Error msg returned from http handler + internet_connection_error_msg = '' # Error msg returned from http handler internet_connection_progress_cnt = 0 # Progress display counter internet_connection_status_request_cnt = 0 # Recheck counter internet_connection_status_request_secs = 0 @@ -276,6 +278,7 @@ class GlobalVariables(object): log_debug_flag_restart = None log_rawdata_flag_restart = None evlog_trk_monitors_flag = False + evlog_startup_log_flag = False info_notification = '' ha_notification = {} trace_prefix = '_INIT_' @@ -312,7 +315,6 @@ class GlobalVariables(object): time_zone_offset_secs = 0 time_zone_offset_str = '+00:00' time_zone_offset_secs_PST = -8 * 60 * 60 - time_zone_offset_secs_apple_server = 0 # timestamp_local_offset_secs = 0 @@ -492,6 +494,7 @@ class GlobalVariables(object): mobapp_update_flag = {} attr_tracking_msg = '' # tracking msg on attributes all_tracking_paused_flag = False + all_tracking_paused_secs = 0 dist_to_other_devices_update_sensor_list = set() # Contains a list of devicenames that need their distance sensors updated # at the end of polling loop after all devices have been processed diff --git a/custom_components/icloud3/helpers/messaging.py b/custom_components/icloud3/helpers/messaging.py index bd61224..da9be43 100644 --- a/custom_components/icloud3/helpers/messaging.py +++ b/custom_components/icloud3/helpers/messaging.py @@ -1053,7 +1053,7 @@ def _called_from(trace=False): # LOG FILE PASSWORD FILTER # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -def add_log_file_filter(item, hide_text=None): +def add_log_file_filter(item, replacement_text=None): ''' Set up log file filter to replace the items value with *'s or prevent it from displaying in the log file. If item contains an '@' and it is being hidden, @@ -1064,13 +1064,27 @@ def add_log_file_filter(item, hide_text=None): Note ''' - if item is not None: - if hide_text is None: - list_add(Gb.log_file_filter_items, item) + if item is None: + return + + # username/email address - reformat useremailname@gmail.com --> us****me@ + if instr(item, '@'): + email_parts = item.split('@') + Gb.log_file_filter_items[email_parts[1]] = '' + + item = email_parts[0] + if replacement_text is None: + replacement_text = f"{item[:2]}{'*'*4}{item[-2:]}@" else: - if instr(item, '@'): - item = item.split('@')[1] - list_add(Gb.log_file_hide_items, item) + replacement_text = f"{item[:2]}{replacement_text}{item[-2:]}@" + Gb.log_file_filter_items[f"{item}@"] = replacement_text + Gb.log_file_filter_items[item] = replacement_text + + elif replacement_text is not None: + Gb.log_file_filter_items[item] = replacement_text + + else: + Gb.log_file_filter_items[item] = '*'*8 #-------------------------------------------------------------------- class LoggerFilter(logging.Filter): @@ -1083,13 +1097,9 @@ def filter(record): return True message = record.msg - for filtered_item in Gb.log_file_filter_items: - if instr(message, filtered_item): - message = message.replace(filtered_item, '*'*8) - - for filtered_item in Gb.log_file_hide_items: + for filtered_item, replacement_text in Gb.log_file_filter_items.items(): if instr(message, filtered_item): - message = message.replace(filtered_item, '') + message = message.replace(filtered_item, replacement_text) record.msg = message record.args = [] diff --git a/custom_components/icloud3/helpers/time_util.py b/custom_components/icloud3/helpers/time_util.py index a2b0a97..4cef2ab 100644 --- a/custom_components/icloud3/helpers/time_util.py +++ b/custom_components/icloud3/helpers/time_util.py @@ -19,6 +19,12 @@ def time_now_secs(): ''' now --> epoch/unix secs ''' return int(time.time()) +#-------------------------------------------------------------------- + +def time_now_utc_secs(): + ''' now ==> utc time zone (secs)''' + return time_now_secs() - Gb.time_zone_offset_secs + #-------------------------------------------------------------------- def time_now(): ''' now --> epoch/unix 10:23:45 ''' @@ -56,6 +62,7 @@ def datetime_now(datetime_struct=False): else: return str(datetime.fromtimestamp(int(time.time()))) + #-------------------------------------------------------------------- def smh_time(time): smh_time_str = time.replace(' sec', 's').replace(' secs', 's') @@ -499,13 +506,12 @@ def calculate_time_zone_offset(): Gb.time_zone_offset_str = f"{local_zone_offset[0:3]}:{local_zone_offset[3:]}" Gb.time_zone_offset_secs = local_zone_offset_secs - Gb.time_zone_offset_secs_apple_server = \ - Gb.time_zone_offset_secs_PST - Gb.time_zone_offset_secs - post_event( f"Local Time Zone Offset > " - f"UTC{Gb.time_zone_offset_str} hrs, " - f"{local_zone_name}, " - f"Country Code-{Gb.country_code.upper()}") + post_event( f"Local Time Zone > " + f"{local_zone_name} " + f"(UTC{Gb.time_zone_offset_str} hrs), " + f"Country Code-{Gb.country_code.upper()}, " + f"Apple Server Time-{apple_server_time()}") except Exception as err: internal_error_msg(err, 'CalcTimeOffset') @@ -647,8 +653,8 @@ def apple_server_time(): - Feb 17, 2025, 8:19 AM PST - Feb 17, 2025, 08:19 PST ''' - time_apple_server_secs = time_now_secs() + Gb.time_zone_offset_secs_apple_server - time_struct = time.localtime(time_apple_server_secs) + pst_secs = time_now_utc_secs() + Gb.time_zone_offset_secs_PST + time_struct = time.localtime(pst_secs) if Gb.time_format_12_hour: return time.strftime("%b %d, %Y, %-I:%M %p PST", time_struct) diff --git a/custom_components/icloud3/icloud3_main.py b/custom_components/icloud3/icloud3_main.py index 10ba0c1..d3dc0b4 100644 --- a/custom_components/icloud3/icloud3_main.py +++ b/custom_components/icloud3/icloud3_main.py @@ -143,6 +143,7 @@ def start_icloud3(self): Gb.start_icloud3_inprocess_flag = True Gb.restart_icloud3_request_flag = False Gb.all_tracking_paused_flag = False + Gb.all_tracking_paused_secs = 0 start_ic3_control.stage_1_setup_variables() start_ic3_control.stage_2_prepare_configuration() @@ -256,7 +257,8 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): # End - Unccommented code to test of moving device into a statzone while home if Gb.all_tracking_paused_flag: - post_evlog_greenbar_msg('All Devices > Tracking Paused') + post_evlog_greenbar_msg(f"All Devices > Tracking Paused at " + f"{format_time_age(Gb.all_tracking_paused_secs)}") return #<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> @@ -1213,9 +1215,13 @@ def _display_device_alert_evlog_greenbar_msg(self): general_alert_msg += f"{CRLF_LDOT}{devicename} > {error_msg}" for username, PyiCloud in Gb.PyiCloud_by_username.items(): - if PyiCloud and PyiCloud.requires_2fa: - general_alert_msg += ( f"{CRLF_LDOT}Apple Acct > {PyiCloud.account_owner_short}, " - f"Authentication Needed") + if PyiCloud: + if PyiCloud.login_successful is False: + general_alert_msg += ( f"{CRLF_LDOT}Apple Acct > {PyiCloud.account_owner_short}, " + f"Login Failed") + if PyiCloud.requires_2fa: + general_alert_msg += ( f"{CRLF_LDOT}Apple Acct > {PyiCloud.account_owner_short}, " + f"Authentication Needed") if (Gb.icloud_acct_error_cnt > 5 and instr(general_alert_msg, 'errors accessing') is False): @@ -1358,18 +1364,21 @@ def _handle_internet_connection_error(self): for Device in Gb.Devices: Device.pause_tracking() - post_event(f"{EVLOG_ALERT}HomeAsst Offline > Pause all tracking") + post_event( f"{EVLOG_ALERT}Internet Connection Error > Tracking Paused, " + f"{Gb.internet_connection_error_msg} " + f"({Gb.internet_connection_error_code})") # If the Mobile App is set up, send a message to the 1st Device that can use # the notify service - if isnot_empty(Gb.mobapp_id_by_mobapp_dname): - Devices = [Device for Device in Gb.Devices - if Device.is_tracked and Device.mobapp[NOTIFY] != ''] - if isnot_empty(Devices): - message = {"message": "Home Asst Server is Offline due to an Internet " - f"Connection Error, {secs_to_time(time_now_secs())} " - "(iCloud3)"} - mobapp_interface.send_message_to_device(Devices[0], message) + # if isnot_empty(Gb.mobapp_id_by_mobapp_dname): + # Devices = [Device for Device in Gb.Devices + # if Device.is_tracked and Device.mobapp[NOTIFY] != ''] + # if isnot_empty(Devices): + # message = {"message": "Internet Connection Error > iCloud3 Tracking Paused, " + # f"{secs_to_time(time_now_secs())}, " + # f"{Gb.internet_connection_error_msg} " + # f"({Gb.internet_connection_error_code})"} + # mobapp_interface.send_message_to_device(Devices[0], message) return @@ -1397,8 +1406,10 @@ def _handle_internet_connection_error(self): def reset_internet_connection_error(): Gb.internet_connection_error = False Gb.internet_connection_error_secs = 0 - Gb.internet_connection_status_request_cnt = 0 + Gb.internet_connection_error_msg = '' + Gb.internet_connection_error_code = 0 Gb.internet_connection_progress_cnt = 0 + Gb.internet_connection_status_request_cnt = 0 data_source_not_set_Devices = [Device for Device in Gb.Devices @@ -1408,22 +1419,23 @@ def reset_internet_connection_error(): #if isnot_empty(devices_not_setup): notify_Device = None if isnot_empty(data_source_not_set_Devices): - post_event(f"{EVLOG_ALERT}HomeAsst Back Online > Restarting iCloud3") + post_event(f"{EVLOG_ALERT}Internet Connection Available > iCloud3 Restarting") Gb.restart_icloud3_request_flag = True else: - post_event(f"{EVLOG_ALERT}HomeAsst Back Online > Resume tracking") - for Device in Gb.Devices: - Device.resume_tracking() - if (notify_Device is None - and Device.mobapp[NOTIFY] != ''): - notify_Device = Device + post_event(f"{EVLOG_ALERT} Internet Connection Available > Tracking Resumed") + + # for Device in Gb.Devices: + # Device.resume_tracking() + # if (notify_Device is None + # and Device.mobapp[NOTIFY] != ''): + # notify_Device = Device # If the Mobile App is set up, send a message to the 1st Device that can use # the notify service - if notify_Device: - message = {"message": "Home Asst Server is back Online, " - f"{secs_to_time(time_now_secs())} (iCloud3)"} - mobapp_interface.send_message_to_device(notify_Device, message) + # if notify_Device: + # message = {"message": "Internet Connection Available > iCloud3 Tracking Resumed, " + # f"{secs_to_time(time_now_secs())}"} + # mobapp_interface.send_message_to_device(notify_Device, message) #............................................................................... def _internet_connection_status_msg(self): @@ -1436,7 +1448,7 @@ def _internet_connection_status_msg(self): else: Gb.internet_connection_progress_cnt += 1 progress_bar = '🟡'*Gb.internet_connection_progress_cnt - evlog_msg =(f"HOME ASST SERVER IS OFFLINE > Since " + evlog_msg =(f"INTERNET CONNECTION ERROR > Since " f"{format_time_age(Gb.internet_connection_error_secs, xago=True)}" f"{CRLF}Checking-{secs_to_time(Gb.internet_connection_status_request_secs)} " f"(#{Gb.internet_connection_status_request_cnt}) " diff --git a/custom_components/icloud3/manifest.json b/custom_components/icloud3/manifest.json index 685e552..2f4182b 100644 --- a/custom_components/icloud3/manifest.json +++ b/custom_components/icloud3/manifest.json @@ -10,5 +10,5 @@ "issue_tracker": "https://github.com/gcobb321/icloud3/issues", "loggers": ["icloud3"], "requirements": ["srp"], - "version": "3.1.5" + "version": "3.1.6" } diff --git a/custom_components/icloud3/support/config_file.py b/custom_components/icloud3/support/config_file.py index 93ccc83..8a5c869 100644 --- a/custom_components/icloud3/support/config_file.py +++ b/custom_components/icloud3/support/config_file.py @@ -1,7 +1,7 @@ from ..global_variables import GlobalVariables as Gb from ..const import ( - ICLOUD3, ICLOUD_SERVER_COUNTRY_CODE, + ICLOUD3, RARROW, RARROW2, HHMMSS_ZERO, DATETIME_ZERO, NONE_FNAME, INACTIVE_DEVICE, ICLOUD, MOBAPP, NO_MOBAPP, NO_IOSAPP, HOME, CONF_PARAMETER_TIME_STR, @@ -11,7 +11,7 @@ CONF_EVLOG_CARD_DIRECTORY, CONF_EVLOG_CARD_PROGRAM, CONF_TRAVEL_TIME_FACTOR, CONF_UPDATE_DATE, CONF_VERSION_INSTALL_DATE, CONF_USERNAME, CONF_PASSWORD, CONF_LOCATE_ALL, CONF_TOTP_KEY, - CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, + CONF_SERVER_LOCATION, CONF_SERVER_LOCATION_NEEDED, CONF_DEVICES, CONF_IC3_DEVICENAME, CONF_SETUP_ICLOUD_SESSION_EARLY, CONF_UNIT_OF_MEASUREMENT, CONF_TIME_FORMAT, CONF_LOG_LEVEL, CONF_LOG_LEVEL_DEVICES, CONF_DATA_SOURCE, CONF_LOG_ZONES, @@ -243,6 +243,7 @@ def _add_parms_and_check_config_file(): update_config_file_flag = False update_config_file_flag = _config_file_check_new_ic3_version() or update_config_file_flag update_config_file_flag = _update_tracking_parameters() or update_config_file_flag + update_config_file_flag = _update_apple_acct_parameters() or update_config_file_flag update_config_file_flag = _update_device_parameters() or update_config_file_flag update_config_file_flag = _update_general_parameters() or update_config_file_flag @@ -303,11 +304,13 @@ async def async_load_icloud3_ha_config_yaml(ha_config_yaml): def build_log_file_filters(): try: + aa_idx = 0 for conf_apple_acct in Gb.conf_apple_accounts: + aa_idx += 1 if conf_apple_acct[CONF_USERNAME] == '': continue - add_log_file_filter(conf_apple_acct[CONF_USERNAME], hide_text=True) + add_log_file_filter(conf_apple_acct[CONF_USERNAME], f"**{aa_idx}**") add_log_file_filter(conf_apple_acct[CONF_PASSWORD]) add_log_file_filter(decode_password(conf_apple_acct[CONF_PASSWORD])) @@ -346,8 +349,8 @@ def build_initial_config_file_structure(): # Verify general parameters and make any necessary corrections try: - if Gb.country_code in ICLOUD_SERVER_COUNTRY_CODE: - Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] = Gb.country_code + if Gb.country_code in ['cn', 'hk']: + Gb.conf_tracking[CONF_SERVER_LOCATION_NEEDED] = True if Gb.config and Gb.config.units['name'] != 'Imperial': Gb.conf_general[CONF_UNIT_OF_MEASUREMENT] = 'km' @@ -617,8 +620,7 @@ def _update_tracking_parameters(): if is_empty(new_items): return False - log_info_msg( f"Updating Configuration File with New items (Tracking) > " - f"{list_to_str(new_items)}") + log_info_msg("Updating Configuration File with New items (Tracking) > ") for item in new_items: Gb.conf_tracking = _insert_into_conf_dict_parameter( @@ -635,6 +637,35 @@ def _update_tracking_parameters(): return True +#-------------------------------------------------------------------- +def _update_apple_acct_parameters(): + ''' + Update Gb.conf_apple_account with new fields + ''' + + cd_idx = -1 + conf_apple_accts = Gb.conf_apple_accounts.copy() + for conf_apple_acct in conf_apple_accts: + cd_idx += 1 + + new_items = [item for item in DEFAULT_APPLE_ACCOUNT_CONF + if item not in conf_apple_acct] + + if is_empty(new_items): + return False + + log_info_msg("Updating Configuration File with New items (Apple Acct) > ") + + for item in new_items: + conf_apple_acct = _insert_into_conf_dict_parameter( + conf_apple_acct, item, + DEFAULT_APPLE_ACCOUNT_CONF[item], + before=CONF_DATA_SOURCE) + + Gb.conf_apple_accounts[cd_idx] = conf_apple_acct + + return True + #-------------------------------------------------------------------- def _update_device_parameters(): ''' @@ -651,8 +682,7 @@ def _update_device_parameters(): if is_empty(new_items): return False - log_info_msg( f"Updating Configuration File with New items (Device) > " - f"{list_to_str(new_items)}") + log_info_msg("Updating Configuration File with New items (Device) > ") for item in new_items: # v3.1.0 'apple_account' and other fields @@ -661,8 +691,8 @@ def _update_device_parameters(): else: conf_device = _insert_into_conf_dict_parameter( - conf_device, - item, DEFAULT_DEVICE_CONF[item], + conf_device, item, + DEFAULT_DEVICE_CONF[item], before=CONF_TRACK_FROM_BASE_ZONE) Gb.conf_devices[cd_idx] = conf_device @@ -674,8 +704,8 @@ def _v310_device_parameter_updates(conf_device): # v3.1 - Add Apple account parameter if CONF_APPLE_ACCOUNT not in conf_device: conf_device = _insert_into_conf_dict_parameter( - conf_device, - CONF_APPLE_ACCOUNT, Gb.conf_tracking[CONF_USERNAME], + conf_device, CONF_APPLE_ACCOUNT, + Gb.conf_tracking[CONF_USERNAME], before=CONF_FAMSHR_DEVICENAME) @@ -705,16 +735,15 @@ def _update_general_parameters(): if is_empty(new_items): return False - log_info_msg( f"Updating Configuration File with New items (General) > " - f"{list_to_str(new_items)}") + log_info_msg("Updating Configuration File with New items (General) > ") for item in new_items: before_item = _place_item_before(item, DEFAULT_GENERAL_CONF, CONF_DISPLAY_TEXT_AS) Gb.conf_data[CF_GENERAL][item] = DEFAULT_GENERAL_CONF[item] Gb.conf_general = _insert_into_conf_dict_parameter( - Gb.conf_general, - item, DEFAULT_GENERAL_CONF[item], + Gb.conf_general, item, + DEFAULT_GENERAL_CONF[item], before= before_item) return True @@ -751,6 +780,8 @@ def _insert_into_conf_dict_parameter(dict_parameter, before - Insert it before this argument after - Insert it after this argument ''' + log_info_msg(f" - Parameter-{new_item}, Initial Value-{initial_value}") + if isinstance(dict_parameter, dict) is False or not new_item: return dict_parameter if initial_value is None: initial_value = '' @@ -769,7 +800,7 @@ def _insert_into_conf_dict_parameter(dict_parameter, return dict(items) except Exception as err: - _LOGGER.exception(err) + # _LOGGER.exception(err) dict_parameter[new_item] = initial_value return dict_parameter diff --git a/custom_components/icloud3/support/event_log.py b/custom_components/icloud3/support/event_log.py index 5daaf60..6592ecd 100644 --- a/custom_components/icloud3/support/event_log.py +++ b/custom_components/icloud3/support/event_log.py @@ -13,17 +13,18 @@ from ..global_variables import GlobalVariables as Gb from ..const import (HOME, HOME_FNAME, TOWARDS, HHMMSS_ZERO, HIGH_INTEGER, NONE, MOBAPP, - RED_X, YELLOW_ALERT, CIRCLE_LETTERS_DARK, CIRCLE_LETTERS_LITE, + RED_X, RED_ALERT, YELLOW_ALERT, + CIRCLE_LETTERS_DARK, CIRCLE_LETTERS_LITE, NL, NL_DOT, LDOT2, CRLF, CRLF_DOT, CRLF_CHK, RARROW, DOT, LT, GT, DASH_50, NBSP, NBSP2, NBSP3, NBSP4, NBSP5, NBSP6, CLOCK_FACE, EVENT_RECDS_MAX_CNT_BASE, EVENT_LOG_CLEAR_SECS, EVENT_LOG_CLEAR_CNT, EVENT_RECDS_MAX_CNT_ZONE, EVLOG_BTN_URLS, EVLOG_TIME_RECD, EVLOG_HIGHLIGHT, EVLOG_MONITOR, EVLOG_TRACE, - EVLOG_ERROR, EVLOG_ALERT, EVLOG_UPDATE_START, EVLOG_UPDATE_END, + EVLOG_INIT_HDR, EVLOG_UPDATE_START, EVLOG_UPDATE_END, + EVLOG_ALERT, EVLOG_WARNING, EVLOG_ERROR, EVLOG_NOTICE, + EVLOG_HIGHLIGHT, EVLOG_IC3_STARTING, EVLOG_IC3_STAGE_HDR, CONF_EVLOG_BTNCONFIG_URL, - EVLOG_ERROR, EVLOG_ALERT, EVLOG_WARNING, EVLOG_INIT_HDR, - EVLOG_HIGHLIGHT,EVLOG_IC3_STARTING, EVLOG_IC3_STAGE_HDR, ) from ..helpers.common import instr, circle_letter, str_to_list, list_to_str, isbetween @@ -44,6 +45,7 @@ ELR_TIME = 1 ELR_TEXT = 2 MAX_EVLOG_RECD_LENGTH = 2000 + # The text starts with a special character: # ^1^ - LightSeaGreen # ^2^ - BlueViolet @@ -51,11 +53,17 @@ # ^4^ - DeepPink # ^5^ - MediumVioletRed # ^6^ - --dark-primary-color -# EVLOG_NOTICE = "^2^" -# EVLOG_ERROR = "^3^" -# EVLOG_ALERT = "^4^" -# EVLOG_TRACE = "^5^" -# EVLOG_MONITOR = "^6^" +# EVLOG_TIME_RECD = '^t^' # MobileApp State, ic3 Zone, interval, travel time, distance event +# EVLOG_UPDATE_HDR = '^u^' # update start-to-complete highlight and edge bar block +# EVLOG_UPDATE_START= '^s^' # update start-to-complete highlight and edge bar block +# EVLOG_UPDATE_END = '^c^' # update start-to-complete highlight and edge bar block +# EVLOG_ERROR = '^e^' +# EVLOG_ALERT = '^a^' +# EVLOG_WARNING = '^w^' +# EVLOG_INIT_HDR = '^i^' # iC3 initialization start/complete event +# EVLOG_HIGHLIGHT = '^h^' # Display item in green highlight bar +# EVLOG_IC3_STARTING = '^i^' +# EVLOG_IC3_STAGE_HDR = '^g^' MONITORED_DEVICE_EVENT_FILTERS = [ 'iCloud Acct Auth', @@ -359,15 +367,32 @@ def post_event(self, devicename_or_Device, event_text='+'): self._add_recd_to_event_recds(event_recd) - if (self.startup_event_save_recd_flag - or (event_text.startswith(EVLOG_ALERT) - and instr(event_text, '> Old') is False) - or instr(event_text, 'Acct Auth')): + if self._startup_error_log_filter(event_text): self._save_startup_log_recd(Device, event_recd) except Exception as err: log_exception(err) +#...................................................... + def _startup_error_log_filter(self, event_text): + + + evlog_alert_type = event_text[:3] + if (self.startup_event_save_recd_flag + or evlog_alert_type in [EVLOG_NOTICE, EVLOG_ERROR, EVLOG_WARNING] + or evlog_alert_type == EVLOG_ALERT and instr(event_text, '> Old') is False): + save_recd_flag = True + else: + save_recd_flag = False + + if save_recd_flag: + if (event_text.startswith('DistTo') + or event_text.startswith('Battery Info') + or event_text.startswith('iCloud Trigger')): + save_recd_flag = False + + return save_recd_flag + #------------------------------------------------------ def _break_up_event_text(self, devicename, this_update_time, event_text, MAX_EVLOG_RECD_LENGTH): ''' @@ -577,10 +602,25 @@ def _add_recd_to_event_recds(self, event_recd): #------------------------------------------------------ def _save_startup_log_recd(self, Device, event_recd): # Add the startup and alert events to a non-clearable table (reset on a restart) - if Device and event_recd[ELR_TEXT].startswith(EVLOG_UPDATE_END): - event_text=(f"{EVLOG_IC3_STAGE_HDR}{Device.fname} > " - f"{event_recd[ELR_TEXT][3:]}") - event_recd = [event_recd[ELR_DEVICENAME], event_recd[ELR_TIME], event_text] + + event_text = event_recd[ELR_TEXT] + if Device: + if event_text.startswith(EVLOG_UPDATE_END): + event_text=(f"{EVLOG_IC3_STARTING}{Device.fname} > " + f"{event_text[3:]}") + if instr(event_text, ','): + event_text = event_text.split(',')[0] + event_recd = [event_recd[ELR_DEVICENAME], event_recd[ELR_TIME], event_text] + + # Add device name on front of text + elif (event_text.startswith(EVLOG_ALERT) + or instr(event_text, RED_X) + or instr(event_text, RED_ALERT)): + if event_text.startswith('^'): + event_text = event_text[3:] + event_text=(f"{ Device.fname} > {event_text}") + event_recd = [event_recd[ELR_DEVICENAME], event_recd[ELR_TIME], event_text] + self.startup_event_recds.insert(0, event_recd) @@ -700,8 +740,11 @@ def _extract_filtered_evlog_recds(self, devicename): Select the items for the device or '*' and return the string of the resulting list to be passed to the Event Log ''' - if devicename == 'startup_log': - self.greenbar_alert_msg=( f"Start up log, alerts and èrrors are displayed" + + # The evlog_startup_log_flag is set in the service_handler when Show + # Startup Log, Errors & Alerts is selected on the EvLog screen + if Gb.evlog_startup_log_flag: + self.greenbar_alert_msg=( f"Start up log, alerts and èrrors" f"{RARROW}Refresh to close") el_recds = [el_recd[1:3] for el_recd in self.startup_event_recds if (el_recd[ELR_TEXT].startswith(EVLOG_MONITOR) is False diff --git a/custom_components/icloud3/support/pyicloud_ic3.py b/custom_components/icloud3/support/pyicloud_ic3.py index e35f2eb..dbbdb89 100644 --- a/custom_components/icloud3/support/pyicloud_ic3.py +++ b/custom_components/icloud3/support/pyicloud_ic3.py @@ -24,7 +24,7 @@ EVLOG_NOTICE, EVLOG_ALERT, LINK, RLINK, LLINK, DOTS, HHMMSS_ZERO, RARROW, PDOT, CRLF, CRLF_DOT, CRLF_STAR, CRLF_CHK, CRLF_HDOT, ICLOUD, NAME, ID, - ICLOUD_SERVER_COUNTRY_CODE, ICLOUD_SERVER_ENDPOINT, + APPLE_SERVER_ENDPOINT, ICLOUD_HORIZONTAL_ACCURACY, LOCATION, TIMESTAMP, LOCATION_TIME, DATA_SOURCE, ICLOUD_BATTERY_LEVEL, ICLOUD_BATTERY_STATUS, BATTERY_STATUS_CODES, @@ -32,7 +32,6 @@ ICLOUD_DEVICE_STATUS, DEVICE_STATUS_CODES, CONF_USERNAME, CONF_APPLE_ACCOUNT, CONF_PASSWORD, CONF_MODEL_DISPLAY_NAME, CONF_RAW_MODEL, - CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, CONF_IC3_DEVICENAME, CONF_FNAME, CONF_FAMSHR_DEVICENAME, CONF_FAMSHR_DEVICE_ID, CONF_LOG_LEVEL_DEVICES, ) @@ -177,15 +176,16 @@ def __init__(self): self.username = 'validate_upw' self.password = 'validate_upw' + self.client_id = f"auth-{str(uuid1()).lower()}" self.ValidationPyiCloudSession = pyi_session.PyiCloudSession(self, validate_aa_upw=True) self.ValidationPyiCloud = PyiCloudService( self.username, self.password, - icloud_server_suffix=Gb.icloud_server_suffix, + apple_server_location='usa', validate_aa_upw=True) - # Gb.AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth" + # self.AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth" self.response_code = 0 self.last_response_code = 0 @@ -297,7 +297,7 @@ def validate_with_verify_via_url(self, username, password): 'User-Agent': 'Apple-iCloud/9.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/605.1.15 (KHTML, like Gecko)', 'Authorization': f"Basic {username_password_b64}"} # url = f"https://setup.icloud.com/setup/authenticate/{self.username}" - url = f"{ICLOUD_SERVER_ENDPOINT['auth_url']}/{self.username}" + url = f"{APPLE_SERVER_ENDPOINT['auth_url']}/{self.username}" data = None response_code = self.ValidationPyiCloudSession.post(url, data=data, headers=headers) @@ -366,10 +366,10 @@ class PyiCloudService(): ''' def __init__( self, username, password=None, + apple_server_location=None, locate_all_devices=None, cookie_directory=None, session_directory=None, - icloud_server_suffix=None, validate_aa_upw=False, config_flow_login=False): @@ -423,23 +423,8 @@ def __init__( self, username, password=None, self.authentication_alert_displayed_flag = False self.update_requested_by = '' - # Gb.HOME_ENDPOINT = "https://www.icloud.com" - # Gb.SETUP_ENDPOINT = "https://setup.icloud.com/setup/ws/1" - # Gb.AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth" - - - # This handles the .cn endpoint suffix but can be expanded to other contries if needed - # if endpoint_suffix is None: - # self.endpoint_suffix = '' - # elif endpoint_suffix in ICLOUD_SERVER_COUNTRY_CODE: - # self.endpoint_suffix = endpoint_suffix.lower() - # self._setup_url_endpoint_suffix() - # else: - # self.endpoint_suffix = '' - if icloud_server_suffix is None: - self.icloud_server_suffix = Gb.icloud_server_suffix - else: - self.icloud_server_suffix = icloud_server_suffix + self.apple_server_location = 'usa' if apple_server_location is None else apple_server_location + self._setup_apple_server_url() self.cookie_directory = cookie_directory or Gb.icloud_cookie_directory self.session_directory = session_directory or Gb.icloud_session_directory @@ -477,9 +462,8 @@ def __init__( self, username, password=None, else: err_msg = ( f"Apple Acct > {self.username_base}, Login Failed, " f"{self.response_code_desc}, " + f"AppleServerLocation-`{self.apple_server_location}`, " "Location Data not Refreshed") - if Gb.icloud_server_suffix != '': - err_msg += f", iCloud ServerSuffix-`{Gb.icloud_server_suffix}`" post_error_msg(err_msg) except Exception as err: @@ -522,6 +506,21 @@ def _initialize_variables(self): self.device_model_info_by_fname = {} # {'Gary-iPhone': [raw_model, model, model_display_name]} self.dup_icloud_dname_cnt = {} # Used to create a suffix for duplicate devicenames # {'Gary-iPhone': ['iPhone15,2', 'iPhone', 'iPhone 14 Pro']} + +#------------------------------------------------------------------------------ + def _setup_apple_server_url(self): + ''' + Set up the icloud.com server endpoint urls for China (icloud.com.cn) + ''' + if self.apple_server_location.startswith('.'): + endpoint_suffix = f"icloud.com{self.apple_server_location}" + else: + endpoint_suffix = 'icloud.com' + + self.HOME_ENDPOINT = APPLE_SERVER_ENDPOINT['home'].replace('icloud.com', endpoint_suffix) + self.SETUP_ENDPOINT = APPLE_SERVER_ENDPOINT['setup'].replace('icloud.com', endpoint_suffix) + self.AUTH_ENDPOINT = APPLE_SERVER_ENDPOINT['auth'] + #--------------------------------------------------------------------------- @property def is_DeviceSvc_setup_complete(self): @@ -644,9 +643,8 @@ def authenticate(self, refresh_session=False): err_msg = f"{self.username_base}, Authentication Failed, " if self.response_code == 302: - err_msg += f"{HTTP_RESPONSE_CODES[302]}, " - if Gb.icloud_server_suffix != '': - err_msg += f"iCloud ServerSuffix-`{Gb.icloud_server_suffix}`, " + err_msg += (f"Apple Server Location-`{self.apple_server_location}`, " + f"{HTTP_RESPONSE_CODES[302]}, ") elif (self.auth_method == 'PasswordSRP' and self.response_code == 401): @@ -733,7 +731,7 @@ def _authenticate_with_token(self): return False try: - url = f"{Gb.SETUP_ENDPOINT}/accountLogin" + url = f"{self.SETUP_ENDPOINT}/accountLogin" self.data = self.PyiCloudSession.post(url, params=self.params, data=data) @@ -803,7 +801,7 @@ def authenticate_with_password(self): ''' headers = self._get_auth_headers() - url = f"{Gb.AUTH_ENDPOINT}/signin" + url = f"{self.AUTH_ENDPOINT}/signin" params = {"isRememberMeEnabled": "true"} data = {"accountName": self.username, "password": self.password, @@ -873,7 +871,7 @@ def encode(self) -> bytes: headers = self._get_auth_headers() headers["Accept"] = "application/json, text/javascript" - url = f"{Gb.AUTH_ENDPOINT}/signin/init" + url = f"{self.AUTH_ENDPOINT}/signin/init" data = { 'a': base64.b64encode(A).decode(), 'accountName': uname, @@ -913,7 +911,7 @@ def encode(self) -> bytes: m1 = usr.process_challenge( salt, b ) m2 = usr.H_AMK - url = f"{Gb.AUTH_ENDPOINT}/signin/complete" + url = f"{self.AUTH_ENDPOINT}/signin/complete" data = { "accountName": uname, "c": c, @@ -943,7 +941,7 @@ def _validate_token(self): # log_debug_msg(f"{self.username_base}, Checking session token validity") - url = f"{Gb.SETUP_ENDPOINT}/validate" + url = f"{self.SETUP_ENDPOINT}/validate" data = "null" try: @@ -1069,7 +1067,8 @@ def _setup_PyiCloudSession(self): self.PyiCloudSession = pyi_session.PyiCloudSession(self) self.PyiCloudSession.verify = True - self.PyiCloudSession.headers.update({"Origin":Gb.HOME_ENDPOINT, "Referer":Gb.HOME_ENDPOINT,}) + self.PyiCloudSession.headers.update({"Origin":self.HOME_ENDPOINT, + "Referer":self.HOME_ENDPOINT,}) self.PyiCloudSession.cookies = cookielib.LWPCookieJar(filename=self.cookie_dir_filename) if path.exists(self.cookie_dir_filename): @@ -1088,7 +1087,7 @@ def _setup_PyiCloudSession(self): # Reset the url endpoint suffix if it has changed. This applies to China (.cn) # ''' - # if (self.endpoint_suffix and Gb.HOME_ENDPOINT.endswith(self.endpoint_suffix)): + # if (self.endpoint_suffix and self.HOME_ENDPOINT.endswith(self.endpoint_suffix)): # return # if self.endpoint_suffix in ICLOUD_SERVER_COUNTRY_CODE: @@ -1100,9 +1099,9 @@ def _setup_PyiCloudSession(self): # # Comment out the following line for non-texting # # self.endpoint_suffix = '' - # Gb.HOME_ENDPOINT = f"https://www.icloud.com{self.endpoint_suffix}" - # Gb.SETUP_ENDPOINT = f"https://setup.icloud.com{self.endpoint_suffix}/setup/ws/1" - # Gb.AUTH_ENDPOINT = f"https://idmsa.apple.com/appleauth/auth" + # self.HOME_ENDPOINT = f"https://www.icloud.com{self.endpoint_suffix}" + # self.SETUP_ENDPOINT = f"https://setup.icloud.com{self.endpoint_suffix}/setup/ws/1" + # self.AUTH_ENDPOINT = f"https://idmsa.apple.com/appleauth/auth" #---------------------------------------------------------------------------- ''' @@ -1242,7 +1241,7 @@ def trusted_devices(self): return '''Returns devices trusted for two-step authentication.''' headers = self._get_auth_headers() - url = f"{Gb.SETUP_ENDPOINT}/listDevices" + url = f"{self.SETUP_ENDPOINT}/listDevices" try: data = self.PyiCloudSession.post(url, params=self.params, headers=headers,) @@ -1257,10 +1256,10 @@ def trusted_devices(self): # try: # _log(f"BUILD IN PYICLOUD TRUSTED DEVICES {self.data}") - # url = f"{Gb.SETUP_ENDPOINT}/listDevices" + # url = f"{self.SETUP_ENDPOINT}/listDevices" # return await Gb.hass.async_add_executor_job( # self.PyiCloudSession.get, - # (f"{Gb.SETUP_ENDPOINT}/listDevices" + # (f"{self.SETUP_ENDPOINT}/listDevices" # f"?clientBuildNumber=2021Project52" # f"&clientMasteringNumber=2021B29" # f"&clientId={self.client_id[5:]}")) @@ -1272,7 +1271,7 @@ def trusted_devices(self): # _session_request(self, method, url, **kwargs # request = self.PyiCloudSession.get(url, params=self.params) # return request.json().get("devices") - # url = f"{Gb.SETUP_ENDPOINT}/listDevices" + # url = f"{self.SETUP_ENDPOINT}/listDevices" # request = self.PyiCloudSession.get(url, params=self.params) # return request.json().get("devices") # headers = self._get_auth_headers() @@ -1293,14 +1292,14 @@ def trusted_devices(self): # request = await Gb.hass.async_add_executor_job( # self.PyiCloudSession.get, - # f"{Gb.SETUP_ENDPOINT}/listDevices" + # f"{self.SETUP_ENDPOINT}/listDevices" # f"?clientBuildNumber=2021Project52" # f"&clientMasteringNumber=2021B29" # f"&clientId={self.client_id[5:]}", # headers) # return request.json().get("devices") # request = self.PyiCloudSession.get( - # f"{Gb.SETUP_ENDPOINT}/listDevices", + # f"{self.SETUP_ENDPOINT}/listDevices", # params=self.params, # data=data, # headers=headers) @@ -1326,7 +1325,7 @@ def icloud_dnames(self): def send_verification_code(self, device): '''Requests that a verification code is sent to the given device.''' - url = f"{Gb.SETUP_ENDPOINT}/sendVerificationCode" + url = f"{self.SETUP_ENDPOINT}/sendVerificationCode" data = device self.data = self.PyiCloudSession.post(url, params=self.params, data=data) @@ -1339,7 +1338,7 @@ def validate_2fa_code(self, code): headers = self._get_auth_headers() #{"Accept": "application/json"}) - url = f"{Gb.AUTH_ENDPOINT}/verify/trusteddevice/securitycode" + url = f"{self.AUTH_ENDPOINT}/verify/trusteddevice/securitycode" data = {"securityCode": {"code": code}} try: @@ -1374,7 +1373,7 @@ def trust_session(self): headers = self._get_auth_headers() try: - self.PyiCloudSession.get(f"{Gb.AUTH_ENDPOINT}/2sv/trust", headers=headers,) + self.PyiCloudSession.get(f"{self.AUTH_ENDPOINT}/2sv/trust", headers=headers,) if self._authenticate_with_token(): self.requires_2fa = self._check_2fa_needed diff --git a/custom_components/icloud3/support/pyicloud_ic3_interface.py b/custom_components/icloud3/support/pyicloud_ic3_interface.py index 65fd8ea..ead0ff1 100644 --- a/custom_components/icloud3/support/pyicloud_ic3_interface.py +++ b/custom_components/icloud3/support/pyicloud_ic3_interface.py @@ -7,6 +7,7 @@ ICLOUD, SETTINGS_INTEGRATIONS_MSG, INTEGRATIONS_IC3_CONFIG_MSG, CONF_USERNAME, CONF_PASSWORD, CONF_TOTP_KEY, CONF_LOCATE_ALL, + CONF_SERVER_LOCATION, CONF_TRACKING_MODE, INACTIVE_DEVICE, ) @@ -85,9 +86,10 @@ def retry_apple_acct_login(): for username in Gb.username_pyicloud_503_connection_error: conf_apple_acct, apple_acct_id = config_file.conf_apple_acct(username) password = conf_apple_acct[CONF_PASSWORD] + apple_server_location = conf_apple_acct[CONF_SERVER_LOCATION] locate_all_devices = conf_apple_acct[CONF_LOCATE_ALL] - PyiCloud = log_into_apple_account(username, password, locate_all_devices) + PyiCloud = log_into_apple_account(username, password, apple_server_location, locate_all_devices) if PyiCloud and PyiCloud.is_authenticated: post_event(f"{EVLOG_ERROR}Apple Acct > {PyiCloud.account_owner}, Login Successful") @@ -136,7 +138,7 @@ def check_all_apple_accts_valid_upw(): Gb.startup_lists['Gb.username_valid_by_username'] = Gb.username_valid_by_username #-------------------------------------------------------------------- -def log_into_apple_account(username, password, locate_all_devices=None): +def log_into_apple_account(username, password, apple_server_location, locate_all_devices=None): ''' Log in and Authenticate the Apple Account via pyicloud @@ -171,7 +173,7 @@ def log_into_apple_account(username, password, locate_all_devices=None): log_debug_msg(f"{debug_msg_hdr}0, Login Started, {pyicloud_msg}") if Gb.internet_connection_error: - post_event( f"{EVLOG_ALERT}HOME ASST SERVER IS OFFLINE > " + post_event( f"{EVLOG_ALERT}INTERNET CONNECTION ERROR > " f"Apple Acct not available:" f"{CRLF_DOT}{username_base}") return None @@ -215,6 +217,7 @@ def log_into_apple_account(username, password, locate_all_devices=None): PyiCloud = None PyiCloud = PyiCloudService( username, password, + apple_server_location=apple_server_location, locate_all_devices=locate_all_devices, cookie_directory=Gb.icloud_cookie_directory, session_directory=Gb.icloud_session_directory) @@ -231,9 +234,9 @@ def log_into_apple_account(username, password, locate_all_devices=None): log_exception(err) if Gb.internet_connection_error: - post_event( f"{EVLOG_ALERT}HOME ASST SERVER IS OFFLINE > " - f"Apple Acct unavailable:" - f"{CRLF_DOT}{username_base}") + post_event( f"{EVLOG_ALERT}INTERNET CONNECTION ERROR > " + f"Apple Acct unavailable:" + f"{CRLF_DOT}{username_base}") return None if PyiCloud.DeviceSvc: @@ -259,8 +262,10 @@ def log_into_apple_account(username, password, locate_all_devices=None): f"{PyiCloud.auth_method}") else: retry_at = secs_to_time(time_now_secs() + 900) - post_event( f"{EVLOG_ALERT}Apple Acct > {username_base}, Login Failed, " - f"{CRLF_DOT}{PyiCloud.response_code_desc}") + post_event( f"{EVLOG_ALERT}Apple Acct > {PyiCloud.account_owner}, Login Failed" + f"{CRLF_DOT}Apple Server Location-`{PyiCloud.apple_server_location}`" + f"{CRLF_DOT}{PyiCloud.response_code_desc}") + post_startup_alert(f"Apple Acct {PyiCloud.account_owner}, Login Failed") verify_icloud_device_info_received(PyiCloud) is_authentication_2fa_code_needed(PyiCloud, initial_setup=True) diff --git a/custom_components/icloud3/support/pyicloud_session.py b/custom_components/icloud3/support/pyicloud_session.py index f3fb02d..66cff90 100644 --- a/custom_components/icloud3/support/pyicloud_session.py +++ b/custom_components/icloud3/support/pyicloud_session.py @@ -207,6 +207,9 @@ def request(self, method, url, **kwargs): # pylint: disable=arguments-differ f"iCloudServerSuffix-`{Gb.icloud_server_suffix}`, " f"Error-{err}") + Gb.internet_connection_error_msg = err + Gb.internet_connection_error_code = self.response_code + self.response_code = -3 self.PyiCloud.response_code = -3 self.response_ok = False @@ -236,6 +239,8 @@ def request(self, method, url, **kwargs): # pylint: disable=arguments-differ f"Error-{err}") Gb.internet_connection_error = True + Gb.internet_connection_error_msg = err + Gb.internet_connection_error_code = self.response_code self.response_code = -3 self.PyiCloud.response_code = -3 diff --git a/custom_components/icloud3/support/service_handler.py b/custom_components/icloud3/support/service_handler.py index e5377ed..fbde9c7 100644 --- a/custom_components/icloud3/support/service_handler.py +++ b/custom_components/icloud3/support/service_handler.py @@ -318,12 +318,14 @@ def update_service_handler(action_entry=None, action_fname=None, devicename=None if action == CMD_PAUSE: if devicename is None: Gb.all_tracking_paused_flag = True + Gb.all_tracking_paused_secs = time_now_secs() # Gb.EvLog.display_user_message('Tracking is Paused', alert=True) for Device in Devices: Device.pause_tracking() elif action == CMD_RESUME: Gb.all_tracking_paused_flag = False + Gb.all_tracking_paused_secs = 0 Gb.EvLog.display_user_message('', clear_evlog_greenbar_msg=True) for Device in Devices: Device.resume_tracking() @@ -340,12 +342,22 @@ def update_service_handler(action_entry=None, action_fname=None, devicename=None return + # Display the startup log selected if devicename == 'startup_log': + Gb.evlog_startup_log_flag = True pass + + # Another option selected, startup log already displayed + # Keep Startup log display but also display other selection elif (Gb.EvLog.evlog_attrs['fname'] == 'Startup Events' and action == 'log_level' and action_option == 'monitor'): devicename = 'startup_log' + Gb.evlog_startup_log_flag = True + + # Regular event log displayed + else: + Gb.evlog_startup_log_flag = False Gb.EvLog.update_event_log_display(devicename) diff --git a/custom_components/icloud3/support/start_ic3.py b/custom_components/icloud3/support/start_ic3.py index ed3bef8..e88e456 100644 --- a/custom_components/icloud3/support/start_ic3.py +++ b/custom_components/icloud3/support/start_ic3.py @@ -15,7 +15,6 @@ CRLF_RED_ALERT, RED_ALERT, YELLOW_ALERT, UNKNOWN, RARROW, NBSP2, NBSP4, NBSP6, CIRCLE_STAR, INFO_SEPARATOR, DASH_20, CHECK_MARK, ICLOUD, FAMSHR, - ICLOUD_SERVER_COUNTRY_CODE, ICLOUD_SERVER_ENDPOINT, DEVICE_TYPE_FNAME, DEVICE_TYPE_FNAMES, IPHONE, IPAD, IPOD, WATCH, AIRPODS, MOBAPP, NO_MOBAPP, ICLOUD_DEVICE_STATUS, TIMESTAMP, @@ -29,7 +28,7 @@ CONF_EVLOG_CARD_DIRECTORY, CONF_EVLOG_CARD_PROGRAM, CONF_EVLOG_BTNCONFIG_URL, PICTURE_WWW_STANDARD_DIRS, CONF_PICTURE_WWW_DIRS, CONF_APPLE_ACCOUNT, CONF_USERNAME, CONF_PASSWORD, - CONF_DATA_SOURCE, CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, + CONF_DATA_SOURCE, CONF_DEVICE_TYPE, CONF_RAW_MODEL, CONF_MODEL, CONF_MODEL_DISPLAY_NAME, CONF_INZONE_INTERVALS, CONF_TRACK_FROM_ZONES, CONF_UNIT_OF_MEASUREMENT, CONF_TIME_FORMAT, @@ -360,11 +359,7 @@ def initialize_on_initial_load(): if Gb.initial_icloud3_loading_flag is False: return - # Gb.internet_connection_error = True - # _evlog(f"{Gb.internet_connection_error=}") - Gb.log_level = 'info' - # Gb.username_valid_by_username = {} #------------------------------------------------------------------------------ # @@ -518,12 +513,6 @@ def initialize_data_source_variables(): Gb.username_base = Gb.username.split('@')[0] Gb.password = conf_password Gb.encode_password_flag = Gb.conf_tracking[CONF_ENCODE_PASSWORD] - Gb.icloud_server_suffix = Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] - if Gb.icloud_server_suffix == 'None': - Gb.icloud_server_suffix = '' - Gb.conf_tracking[CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX] = '' - - setup_icloud_server_url(Gb.icloud_server_suffix) Gb.PyiCloud_logging_in_usernames= [] @@ -549,16 +538,6 @@ def initialize_data_source_variables(): Gb.get_ICLOUD_devices_retry_cnt = 0 Gb.startup_alerts = [] -#------------------------------------------------------------------------------ -def setup_icloud_server_url(server_suffix): - ''' - Set up the icloud.com server endpoint urls for China (icloud.com.cn) - ''' - icloud_server_suffix = 'icloud.com' if server_suffix == '' else f"icloud.com.{server_suffix}" - Gb.HOME_ENDPOINT = ICLOUD_SERVER_ENDPOINT['home'].replace('icloud.com', icloud_server_suffix) - Gb.SETUP_ENDPOINT = ICLOUD_SERVER_ENDPOINT['setup'].replace('icloud.com', icloud_server_suffix) - Gb.AUTH_ENDPOINT = ICLOUD_SERVER_ENDPOINT['auth'] - #------------------------------------------------------------------------------ def set_primary_data_source(data_source): ''' @@ -1331,7 +1310,7 @@ def setup_data_source_ICLOUD(retry=False): the account info to display. ''' if Gb.internet_connection_error: - post_startup_alert(f"HOME ASSISTANT SERVER IS OFFLINE") + post_startup_alert(f"INTERNET CONNECTION ERROR") apple_acct_not_found_msg = '' for username, PyiCloud in Gb.PyiCloud_by_username.items(): if is_empty(PyiCloud.RawData_by_device_id): diff --git a/custom_components/icloud3/support/start_ic3_control.py b/custom_components/icloud3/support/start_ic3_control.py index 56066ec..00d6f66 100644 --- a/custom_components/icloud3/support/start_ic3_control.py +++ b/custom_components/icloud3/support/start_ic3_control.py @@ -6,7 +6,7 @@ EVLOG_ALERT, EVLOG_ERROR, EVLOG_IC3_STARTING, EVLOG_IC3_STAGE_HDR, NBSP6, DOT, SETTINGS_INTEGRATIONS_MSG, INTEGRATIONS_IC3_CONFIG_MSG, CONF_VERSION, ICLOUD, ZONE_DISTANCE, - CONF_USERNAME, CONF_PASSWORD, CONF_LOCATE_ALL, + CONF_USERNAME, CONF_PASSWORD, CONF_LOCATE_ALL, CONF_SERVER_LOCATION, ICLOUD, MOBAPP, DISTANCE_TO_DEVICES, ) @@ -27,7 +27,7 @@ _evlog, _log, more_info, format_filename, write_config_file_to_ic3log, open_ic3log_file, ) -from ..helpers.time_util import (time_now, time_now_secs, secs_to_time, calculate_time_zone_offset, ) +from ..helpers.time_util import (time_now, time_now_secs, secs_to_time, ) import homeassistant.util.dt as dt_util @@ -97,7 +97,6 @@ def stage_1_setup_variables(): post_monitor_msg(f"LocationInfo-{Gb.ha_location_info}") - calculate_time_zone_offset() start_ic3.set_event_recds_max_cnt() post_event(f"{EVLOG_IC3_STAGE_HDR}{stage_title}") @@ -323,6 +322,10 @@ def stage_4_setup_data_sources_retry(final_retry=False): PyiCloud = Gb.PyiCloud_by_username.get(username) if PyiCloud: + # If Connection was refused, do not retry logging into apple acct + if PyiCloud.response_code == 302: + continue + post_event(f"Verify Apple Acct > {PyiCloud.account_owner_username}, Verified") start_ic3.setup_data_source_ICLOUD(retry=True) start_ic3.set_devices_verified_status() @@ -384,6 +387,7 @@ def _log_into_apple_accounts(retry=False): PyiCloud = pyicloud_ic3_interface.log_into_apple_account( username, Gb.PyiCloud_password_by_username[username], + apple_server_location=conf_apple_acct[CONF_SERVER_LOCATION], locate_all_devices=conf_apple_acct[CONF_LOCATE_ALL]) if PyiCloud: diff --git a/custom_components/icloud3/translations/en.json b/custom_components/icloud3/translations/en.json index 655435d..6f0a95c 100644 --- a/custom_components/icloud3/translations/en.json +++ b/custom_components/icloud3/translations/en.json @@ -7,6 +7,7 @@ "disabled": "iCloud3 is DISABLED and can not be installed again. Enable iCloud3, then select CONFIGURE in the iCloud3 Integration entry to configure iCloud3.\n\nIf you are deleting and then reinstalling, restart HA first and then reinstall iCloud3", "login_error": "An error occurred logging into the Apple Account. Verify the username and password.", "reauth_successful": "The reauthentication has been successfully completed", + "verification_code_requested": "A New Apple ID Verification Code was requested", "verification_code_accepted": "✅ The Apple ID Verification Code was accepted. Reauthentication is complete", "verification_code_cancelled": "The Apple ID Verification was cancelled", "update_cancelled": "Update Cancelled", @@ -17,7 +18,7 @@ "error": { "invalid_path": "The path provided is not valid. Should be in the format `user/repo-name`", "verification_code_send_error": "Failed to send the Apple ID Verification Code", - "verification_code_requested2": "The Apple ID Verification Code was requested", + "verification_code_requested": "A New Apple ID Verification Code was requested", "verification_code_accepted": "The Apple ID Verification Code was accepted. Reauthentication is complete", "verification_code_invalid": "The Verification Code is not correct. Reenter or request a new code", "verification_code_needed": "The Apple Account Verification Code is needed", @@ -83,24 +84,23 @@ "icloud_acct_login_error_other": "❌ Login Error, Other Error or iCloud is not Available", "icloud_acct_login_error_503": "🍎 Apple is delaying displaying a new Verification code to prevent Suspicious Activity, probably due to too many requests. It should be displayed in about 20-30 minutes. Restart HA if it is not displayed", "icloud_acct_login_error_302": "❌ Login Failed, Apple server refused connection (Error 302)", - "icloud_acct_login_error_302_cn": "❌ Login Failed, Apple server refused connection. CHINA was disabled but Apple server country-code = CHN (Error 302)", - "icloud_acct_login_error_302_xcn": "❌ Login Failed, Apple server refused connection. CHINA was enabled but Apple server country-code is not CHN (Error 302)", + "icloud_acct_login_error_302_cn": "Apple Acct Server may be China but CHINA was not selected", + "icloud_acct_login_error_302_usa": "China was selected but Apple Server Location may be USA", "icloud_acct_login_error_srp_401": "❌ Python SRP Library Credentials Error. The Python module that creates the Secure Remote Password hash key has calculated an incorrect value for a valid Username/Password. Try changing the Password to see if the Apple Acct can be logged into.", "icloud_acct_dup_username_error": "Error: Username is being used by another Data Source entry", "icloud_acct_username_inuse_error": "Error: This Username is being used by another Data Source entry. This one cannot be changed to it until the other one is removed. Select the other one and STOP USING it first.", "icloud_acct_not_available": "❌ Login Failed, Apple Account is not Available", - "icloud_acct_not_logged_into": "Not logged into the Apple Account", + "icloud_acct_not_logged_into": "The Apple Account(s) are not logged into", "icloud_acct_updated_not_logged_into": "Apple Acct info was saved, Login Error, Will Complete Login Later", - "icloud_acct_data_source_warning": "Apple Acct is not selected as a data source, username/password are setup", + "icloud_acct_data_source_warning": "Warning: Apple Accts have been set up but are not used as a data source", "icloud_acct_not_set_up": "No Apple Accts are set up. See `Data Sources > Apple Acct & Mobile App` screen", "icloud_acct_parm_error": "Apple Account parameter error. Update it on the `Data Sources > Apple Account & Mobile App` screen", - "icloud_acct_no_data_source": "❌ No Data Source has been selected (Apple iCloud Account or Mobile App)", + "icloud_acct_no_data_source": "❌ No Data Sources have been selected (Apple Account or Mobile App)", "ic3_icloud_same_name": "iCloud3 dev_trkr.entity_id and name on the device (Settings > General > About) can not be exactly the same (letters & case)", "mobile_app_error": "Error, The Mobile App Integration is not installed. The Mobile App will not be used as a data source; location data and zone enter/exit triggers will not be monitored", "password_asp_invalid": "Apple does not support App Specific Passwords in WebAuth applications", - "verification_code_requested": "The Apple Account Verification Code was requested, BROWSER REFRESH MAY BE NEEDED", - "verification_code_requested2": "The Apple Account Verification Code was requested", + "verification_code_requested": "A New Apple ID Verification Code was requested", "verification_code_needed": "One of the Apple Accounts has requested a Verification Code", "verification_code_accepted": "✅ The Apple Account Verification Code was accepted", "verification_code_invalid": "❌ The Verification Code was not correct. Reenter or request a new code", @@ -220,13 +220,13 @@ } }, "data_source": { - "title": "Data Source - Apple Account, Mobile App", + "title": "Apple Account & Mobile App - Data Source", "description": "The data sources provide location and other information iCloud3 uses to track the iDevice.", "data": { - "data_source": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DATA SOURCES", - "data_source_icloud": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DATA SOURCES", - "data_source_mobapp": "", - "apple_accts": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ APPLE ICLOUD ACCOUNTS", + "data_source": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DATA SOURCE", + "data_source_mobapp": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ MOBILE APP INTEGRATION", + "data_source_apple_acct": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ APPLE ICLOUD ACCOUNTS", + "apple_accts": " ", "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" }, "data_description": { @@ -238,13 +238,14 @@ "account_selected": "ACCOUNT SELECTED", "username": "USERNAME - Email address/username used to sign in to the Apple Account", "password": "PASSWORD - Account Password", + "server_location": "APPLE SERVER LOCATION - The country hosting this account's Apple Server", "totp_key": "TOTP KEY - Time Based One-Time Password Key used to generate the 6-digit authentication code", - "locate_all": "ALWAYS LOCATE ALL DEVICES - Only applies with multiple Apple Accounts -- Enabled=Locate all the devices in the Apple Acct (Owners and Family Devices). Disabled=Only locate the Apple Acct owners devices", + "locate_all": "LOCATE ALL DEVICES (Only applies with multiple Apple Accounts)", "url_suffix_china": "CHINA USERS - Use Apple iCloud Servers located in China (.cn URL suffix)", "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" }, "data_description": { - "locate_all": "Devices fall into two categories, devices belonging to the Apple acct owner and devices in the Family Sharing list. Locating the owners devices = More internet calls, reduces response time; Locating all devices = fewer internet calls, slightly longer response time." + "locate_all": "ENABLED→Locate the Owner's & other Family members devices in the Apple Acct. DISABLED→Locate the Owner's devices. Do not locate other Family members devices." } }, "delete_apple_acct": { @@ -416,7 +417,7 @@ } }, "format_settings": { - "title": "FIELD FORMATS & EVENT LOG OVERRIDES", + "title": "FIELD FORMATS & OTHER PARAMETERS", "description": "Tracking activity, results and information messages are displayed in the Event log, sensors and device_tracker entities for tracked and monitored devices.\n\nThis screen us used to specify how these results should be displayed.", "data": { "display_zone_format": "EVENT LOG ZONE DISPLAY NAME - How the Zone name is displayed in sensors and the Event Log", @@ -425,6 +426,7 @@ "unit_of_measurement": "UNIT OF MEASUREMENT - How distance fields are displayed in sensors and in the Event Log", "display_gps_lat_long2": "DISPLAY GPS COORDINATES - Display the GPS (Latitude, Longitude/±Accuracy) or only the GPS (/±Accuracy) in the Event Log", "display_gps_lat_long": "DISPLAY GPS COORDINATES - Display GPS-(22.32771, -76.33073/±35m) instead of GPS-/±35m in the Event Log", + "apple_server_location_needed": "APPLE SERVER LOCATION - Display Apple Server Location Selection field on Apple Acct Username/Password screen (China, Hong Kong)", "evlog_header": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ EVENT LOG SYSTEM OVERRIDES", "picture_www_dirs": "PICTURE DIRECTORY FILTER - Select the directories containg Device image files (.png, .jpg)", "event_log_card_directory": "EVENT LOG CARD LOVELACE RESOURCES DIRECTORY - Event Log custom card .js file directory",