From f968a71363478f8b6865db2bb3a1a78444e27706 Mon Sep 17 00:00:00 2001 From: gcobb321 Date: Thu, 7 Nov 2024 11:50:38 -0500 Subject: [PATCH] iCloud3 v3.1.1 --- README.md | 2 +- custom_components/icloud3/ChangeLog.txt | 15 + custom_components/icloud3/__init__.py | 56 +- custom_components/icloud3/config_flow.py | 246 +++++--- .../icloud3/config_flow_forms.py | 15 +- custom_components/icloud3/const.py | 41 +- .../icloud3/const_config_flow.py | 12 +- custom_components/icloud3/device.py | 10 +- custom_components/icloud3/device_tracker.py | 62 +- custom_components/icloud3/global_variables.py | 1 + .../icloud3/helpers/messaging.py | 35 +- custom_components/icloud3/icloud3_main.py | 7 - custom_components/icloud3/manifest-dev.json | 14 - custom_components/icloud3/strings - Copy.json | 554 ++++++++++++++++++ custom_components/icloud3/strings.json | 8 +- .../icloud3/support/config_file.py | 33 +- .../icloud3/support/icloud_data_handler.py | 9 +- .../icloud3/support/pyicloud_ic3.py | 314 +++++----- .../icloud3/support/pyicloud_ic3_interface.py | 93 ++- .../icloud3/support/start_ic3.py | 26 +- .../icloud3/support/start_ic3_control.py | 16 +- .../icloud3/support/zone_handler.py | 6 +- .../icloud3/translations/en.json | 8 +- 23 files changed, 1190 insertions(+), 393 deletions(-) delete mode 100644 custom_components/icloud3/manifest-dev.json create mode 100644 custom_components/icloud3/strings - Copy.json diff --git a/README.md b/README.md index 9ebddda..7d438c4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ------ -[![CurrentVersion](https://img.shields.io/badge/Current_Version-v3.1-blue.svg)](https://github.com/gcobb321/icloud3) [![Type](https://img.shields.io/badge/Type-Custom_Component-orange.svg)](https://github.com/gcobb321/icloud3) [![HACS](https://img.shields.io/badge/HACS-Standard_Repository-orange.svg)](https://github.com/gcobb321/icloud3) +[![CurrentVersion](https://img.shields.io/badge/Current_Version-v3.1.1-blue.svg)](https://github.com/gcobb321/icloud3) [![Type](https://img.shields.io/badge/Type-Custom_Component-orange.svg)](https://github.com/gcobb321/icloud3) [![HACS](https://img.shields.io/badge/HACS-Standard_Repository-orange.svg)](https://github.com/gcobb321/icloud3) [![ProjectStage](https://img.shields.io/badge/Project_Stage-General_Availability-forestgreen.svg)](https://github/gcobb321/icloud3) [![Released](https://img.shields.io/badge/Released-November,_2024-forestgreen.svg)](https://github.com/gcobb321/icloud3) diff --git a/custom_components/icloud3/ChangeLog.txt b/custom_components/icloud3/ChangeLog.txt index 7a90fc3..00604f0 100644 --- a/custom_components/icloud3/ChangeLog.txt +++ b/custom_components/icloud3/ChangeLog.txt @@ -3,6 +3,21 @@ **Installing for the first time_** - See [here](https://gcobb321.github.io/icloud3_v3_docs/#/chapters/3.2-installing-and-configuring) for instructions on installing as a New Installation **iCloud3 v3 Documentation** - iCloud3 User Guide can be found [here](https://gcobb321.github.io/icloud3_v3_docs/#/) + + +3.1.1 +....................... +1. LOCATING DEVICES: + - Added additional checks to insure the Apple Account location data was refreshed during startup and while configuing iCloud3 settings. Fixed a problem where the location information data from Apple was not being initialized properly. + - Fixed the location refresh not being causied the '0 of 0' to be displayed in the Configure Setting screens, leading to the Apple Account selection lists to not be populated. + - Fixed some problems where 'Locate All Devices = False' would still locate all the devices in the Apple account + - Added error checking to make sure the Locate All Devices can not be disabled if there were Family devices that + were asigned to that Apple account. If it was disabled, they would never be locaed. +2. UPDATE DEVICE SCREEN - Added (and fixed) the TOOLS - RESET DATA SOURCE(S), DELETE DEVICE(S) option where you can reset the device's Apple Account and Mobile App to default values (None) and fixed a problem deleting devices. Reworked the Apple Acccount selection list to provide more information and identify setup errors. +3. UPDATE APPLE ACCOUNT USERNAME/PASSWORD SCREEN - Added checks to insure Locating All Devices can not be disabled if there are tracked devices assigned to this account that are in the Family list. +4. OTHER THINGS - Changed several things under the covers. + + 3.1 ....................... ### Change Log - v3.1 diff --git a/custom_components/icloud3/__init__.py b/custom_components/icloud3/__init__.py index d6c51fb..eea5038 100644 --- a/custom_components/icloud3/__init__.py +++ b/custom_components/icloud3/__init__.py @@ -29,12 +29,14 @@ log_exception_HA, log_exception) from .helpers.time_util import (time_now_secs, ) 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, make_directory, directory_exists, copy_file, file_exists, rename_file, move_files, ) from .support.v2v3_config_migration import iCloud3_v2v3ConfigMigration from .support import start_ic3 from .support import config_file +from . import device_tracker as ic3_device_tracker from .support import restore_state from .support.service_handler import register_icloud3_services from .support import pyicloud_ic3_interface @@ -135,6 +137,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): Gb.entry_id = entry.entry_id Gb.operating_mode = MODE_INTEGRATION Gb.PyiCloud = None + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) start_ic3.initialize_directory_filenames() @@ -181,9 +184,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): Gb.EvLog.display_user_message(f"Starting {ICLOUD3_VERSION_MSG}") if Gb.use_data_source_ICLOUD: - await _rename_icloud_v30_cookie_directory() - Gb.hass.async_add_executor_job( - pyicloud_ic3_interface.verify_all_apple_accounts) + await Gb.hass.async_add_executor_job( + move_icloud_cookies_to_icloud3_apple_acct) + await Gb.hass.async_add_executor_job( + pyicloud_ic3_interface.verify_all_apple_accounts) # pyicloud_ic3_interface.create_all_PyiCloudServices) # set_up_default_area_id() @@ -298,7 +302,7 @@ async def async_get_ha_location_info(hass): pass #------------------------------------------------------------------------------------------- -async def _rename_icloud_v30_cookie_directory(): +def move_icloud_cookies_to_icloud3_apple_acct(): ''' iCloud3 v3.1 uses the '.../apple_acct.ic3' cookie directory instead of the '.../icloud' directory to avoid conflicts with Apple Account integrations (iCloud, HomeKit, etc) @@ -309,20 +313,15 @@ async def _rename_icloud_v30_cookie_directory(): if the '.../icloapple_acct.ic3/' directory does not exist. create it. ''' try: - # v30_icloud_config_dir = Gb.ha_storage_icloud3.replace('.config', '') - # v31_ic3_config_dir_exists = await async_directory_exists(Gb.ha_storage_icloud3) - # if v31_ic3_config_dir_exists is False: - # Gb.HALogger.info(f"{v30_icloud_config_dir} --> {Gb.ha_storage_icloud3}") - # move_files(v30_icloud_config_dir, Gb.ha_storage_icloud3) - - v31_cookie_dir = Gb.icloud_cookie_directory - v31_cookie_dir_exists = await async_directory_exists(v31_cookie_dir) + + v31_cookie_dir = f"{Gb.icloud_cookie_directory}" + v31_cookie_dir_exists = directory_exists(v31_cookie_dir) if v31_cookie_dir_exists: return - await async_make_directory(v31_cookie_dir) + make_directory(v31_cookie_dir) v30_cookie_dir = Gb.hass.config.path(Gb.ha_storage_directory, 'icloud') - v30_cookie_dir_exists = await async_directory_exists(v30_cookie_dir) + v30_cookie_dir_exists = directory_exists(v30_cookie_dir) Gb.HALogger.info(f"{v30_cookie_dir=} {v30_cookie_dir_exists=}") if v30_cookie_dir_exists is False: @@ -335,10 +334,13 @@ async def _rename_icloud_v30_cookie_directory(): cookie_filename = "".join([c for c in username if match(r"\w", c)]) Gb.HALogger.info(f"{cookie_filename=}") - v30_cookie_filename = f"{v30_cookie_dir}/{cookie_filename}" + v30_cookie_filename = f"{v30_cookie_dir}/{cookie_filename}" v30_cookie_tpw_filename = f"{v30_cookie_dir}/session/{cookie_filename}.tpw" - v30_session_filename = f"{v30_cookie_dir}/session/{cookie_filename}" - v30_tpw_file_exists = await async_directory_exists(v30_cookie_tpw_filename) + v30_session_filename = f"{v30_cookie_dir}/session/{cookie_filename}" + v30_tpw_file_exists = directory_exists(v30_cookie_tpw_filename) + v31_cookie_filename = f"{v31_cookie_dir}/{cookie_filename}" + v31_cookie_tpw_filename = f"{v31_cookie_dir}/{cookie_filename}.tpw" + v31_session_filename = f"{v31_cookie_dir}/{cookie_filename}.session" Gb.HALogger.info(f"{v30_cookie_filename =}") Gb.HALogger.info(f"{v30_cookie_tpw_filename =}") @@ -347,16 +349,20 @@ async def _rename_icloud_v30_cookie_directory(): if v30_tpw_file_exists is False: return - Gb.HALogger.info(f"{v30_cookie_filename} --> {v31_cookie_dir}/{cookie_filename}.session") - await async_copy_file(v30_cookie_filename, f"{v31_cookie_dir}/{cookie_filename}") + Gb.HALogger.info(f"{v30_cookie_filename} --> {v31_session_filename}") + data = read_json_file(v30_session_filename) + save_json_file(v31_session_filename, data) + + Gb.HALogger.info(f"{v30_cookie_tpw_filename} --> {v31_cookie_tpw_filename}") + data = read_json_file(v30_cookie_tpw_filename) + save_json_file(v31_cookie_tpw_filename, data) - Gb.HALogger.info(f"{v30_session_filename} --> {v31_cookie_dir}/{cookie_filename}.session") - await async_copy_file(v30_session_filename, f"{v31_cookie_dir}/{cookie_filename}.session") - # await async_rename_file(v30_session_filename, f"{v30_cookie_dir}/{cookie_filename}.session") + Gb.HALogger.info(f"{v30_cookie_filename} --> {v31_cookie_filename}") + with open(v30_cookie_filename, 'r') as v30_file: + data = v30_file.read() - Gb.HALogger.info(f"{v30_session_filename}.tpw --> {v31_cookie_dir}/{cookie_filename}.tpw") - await async_copy_file(f"{v30_session_filename}.tpw", f"{v31_cookie_dir}/{cookie_filename}.tpw") - # await async_rename_file(f"{v30_session_filename}.tpw", f"{v30_cookie_dir}/{cookie_filename}.tpw") + with open(v31_cookie_filename, 'w') as v31_file: + v31_file.write(data) post_monitor_msg(f"Cookie Directory > Directory and files were copied " f"from `{v30_cookie_dir}` to `{v31_cookie_dir}`") diff --git a/custom_components/icloud3/config_flow.py b/custom_components/icloud3/config_flow.py index 9aa4e80..f71ee2b 100644 --- a/custom_components/icloud3/config_flow.py +++ b/custom_components/icloud3/config_flow.py @@ -20,7 +20,7 @@ NBSP, RARROW, PHDOT, CRLF_DOT, DOT, HDOT, PHDOT, CIRCLE_STAR, RED_X, YELLOW_ALERT, RED_ALERT, EVLOG_NOTICE, EVLOG_ALERT, EVLOG_ERROR, LINK, LLINK, RLINK, IPHONE_FNAME, IPHONE, IPAD, WATCH, AIRPODS, ICLOUD, OTHER, HOME, FAMSHR, - DEVICE_TYPES, DEVICE_TYPE_FNAME, DEVICE_TRACKER_DOT, + DEVICE_TYPES, DEVICE_TYPE_FNAME, DEVICE_TYPE_FNAMES, DEVICE_TRACKER_DOT, MOBAPP, NO_MOBAPP, TRACK_DEVICE, MONITOR_DEVICE, INACTIVE_DEVICE, NAME, FRIENDLY_NAME, FNAME, TITLE, BATTERY, @@ -44,7 +44,7 @@ CONF_WAZE_REALTIME, CONF_WAZE_HISTORY_DATABASE_USED, CONF_WAZE_HISTORY_TRACK_DIRECTION, CONF_STAT_ZONE_FNAME, CONF_STAT_ZONE_STILL_TIME, CONF_DISPLAY_TEXT_AS, - CONF_IC3_DEVICENAME, CONF_FNAME, CONF_FAMSHR_DEVICENAME, CONF_MOBILE_APP_DEVICE, CONF_FMF_EMAIL, + CONF_IC3_DEVICENAME, CONF_FNAME, CONF_FAMSHR_DEVICENAME, CONF_MOBILE_APP_DEVICE, CONF_FMF_EMAIL,CONF_FMF_DEVICE_ID, CONF_TRACKING_MODE, CONF_INZONE_INTERVAL, CONF_FIXED_INTERVAL, CONF_AWAY_TIME_ZONE_1_OFFSET, CONF_AWAY_TIME_ZONE_1_DEVICES, CONF_AWAY_TIME_ZONE_2_OFFSET, CONF_AWAY_TIME_ZONE_2_DEVICES, @@ -54,7 +54,7 @@ CONF_PARAMETER_TIME_STR, CONF_PARAMETER_FLOAT, CF_PROFILE, CF_TRACKING, CF_GENERAL, DEFAULT_DEVICE_CONF, DEFAULT_GENERAL_CONF, DEFAULT_APPLE_ACCOUNTS_CONF, - DEFAULT_DEVICE_REINITIALIZE_CONF, + DEFAULT_DEVICE_DATA_SOURCE, ) from .const_sensor import (SENSOR_GROUPS ) from .const_config_flow import * @@ -838,7 +838,7 @@ async def async_step_confirm_action(self, user_input=None, Notes: Before calling this function, set the self.multi_form_user_input to the user_input. - This will preserve all parameter changes in the calling screen. They are + This will have all parameter changes in the calling screen. They are returned to the called from step on exit. Action item - The action_item selected on this screen is added to the self.multi_form_user_input variable returned. It is resolved in the calling @@ -1866,8 +1866,17 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): if user_input.get('url_suffix_china') is True else 'None' user_input = self._strip_spaces(user_input, [CONF_USERNAME, CONF_PASSWORD, CONF_TOTP_KEY]) + if (user_input[CONF_LOCATE_ALL] is False + and self._can_disable_locate_all(user_input) is False): + self.errors[CONF_LOCATE_ALL] = 'icloud_acct_locate_all_reqd' + user_input[CONF_LOCATE_ALL] = True + action_item = '' + return await self.async_step_update_apple_acct( + user_input=user_input, + errors=self.errors) + user_input[CONF_USERNAME] = user_input[CONF_USERNAME].lower() - user_input[CONF_TOTP_KEY] = user_input[CONF_TOTP_KEY].upper() + user_input[CONF_TOTP_KEY] = '' #user_input[CONF_TOTP_KEY].upper() username = user_input[CONF_USERNAME] password = user_input[CONF_PASSWORD] ui_apple_acct ={CONF_USERNAME: user_input[CONF_USERNAME], @@ -1914,6 +1923,7 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): 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_LOCATE_ALL] != conf_locate_all and Gb.PyiCloud_by_username.get(username) is not None): self.errors['base'] = 'icloud_acct_logged_into' action_item = '' @@ -1932,7 +1942,8 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): username_password_valid = True aa_login_info_changed = False - self.errors = {} + other_flds_changed = False + # self.errors = {} if action_item == 'log_into_apple_acct': # Apple acct login info changed, validate it without logging in if (conf_username != user_input[CONF_USERNAME] @@ -1943,6 +1954,10 @@ async def async_step_update_apple_acct(self, user_input=None, errors=None): or Gb.PyiCloud_by_username.get(username) is None): aa_login_info_changed = True + if (user_input[CONF_LOCATE_ALL] != self.conf_apple_acct[CONF_LOCATE_ALL] + or user_input[CONF_TOTP_KEY] != self.conf_apple_acct[CONF_TOTP_KEY]): + other_flds_changed = True + if aa_login_info_changed: username_password_valid = \ await self._async_validate_username_password(username, password) @@ -1955,7 +1970,7 @@ 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: + if aa_login_info_changed or other_flds_changed: self._update_conf_apple_accounts(self.aa_idx, user_input) await self._async_write_storage_icloud3_configuration_file() @@ -2111,6 +2126,13 @@ def get_conf_device_names_by_username(self, username): return devicenames_by_username, icloud_dnames_by_username +#------------------------------------------------------------------------------------------- + def _can_disable_locate_all(self, user_input): + famshr_Devices = [Device for Device in Gb.Devices + if Device.family_share_device + and Device.conf_apple_acct_username == user_input[CONF_USERNAME]] + + return is_empty(famshr_Devices) #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # DELETE APPLE ACCOUNT @@ -2329,8 +2351,8 @@ async def reauth_send_verification_code_handler(caller_self, user_input): # iCloud setup screen. If it was changed, another account is being logged into # and it will be restarted when exiting the configurator. if valid_code: - post_event(f"{EVLOG_NOTICE}Apple Acct > {caller_self.PyiCloud.account_owner}, " - f"Code accepted, Verification completed") + post_event( f"{EVLOG_NOTICE}Apple Acct > {caller_self.PyiCloud.account_owner}, " + f"Code accepted, Verification completed") await caller_self._build_icloud_device_selection_list() @@ -2439,9 +2461,9 @@ async def log_into_icloud_account(self, user_input, called_from_step_id=None): self.endpoint_suffix = endpoint_suffix Gb.username_valid_by_username[username] = True - if PyiCloud.DeviceSvc: - PyiCloud.DeviceSvc.refresh_client() - + #if PyiCloud.DeviceSvc: + #PyiCloud.DeviceSvc.refresh_client() + PyiCloud.refresh_icloud_data() start_ic3.dump_startup_lists_to_log() if PyiCloud.requires_2fa or called_from_step_id is None: @@ -2533,9 +2555,10 @@ def create_PyiCloudService_config_flow(username, password, endpoint_suffix): @staticmethod def create_DeviceSvc_config_flow(PyiCloud): - iCloud = PyiCloud.create_DeviceSvc_object(config_flow_login=True) + _DeviceSvc = PyiCloud.create_DeviceSvc_object(config_flow_login=True) + start_ic3.dump_startup_lists_to_log() - return iCloud + return _DeviceSvc #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # RESET PYICLOUD SESSION, GENERATE VERIFICATION CODE @@ -2848,8 +2871,11 @@ async def async_step_delete_device(self, user_input=None, errors=None): elif action_item == 'delete_all_devices': self._delete_all_devices() - elif action_item == 'delete_icloud_mobapp_info': - self._clear_icloud_mobapp_selection_parms() + elif action_item == 'reset_this_device_data_source': + self._reset_this_device_data_source_fields() + + elif action_item == 'reset_all_devices_data_source': + self._reset_all_devices_data_source_fields() if action_item != 'delete_device_cancel': list_add(self.config_parms_update_control, ['tracking', 'restart']) @@ -2861,29 +2887,38 @@ async def async_step_delete_device(self, user_input=None, errors=None): def _delete_this_device(self, conf_device=None): """ Delete the device_tracker entity and associated ic3 configuration """ - if conf_device: - devicename = conf_device[CONF_IC3_DEVICENAME] - self.conf_device = conf_device - self.conf_device_idx = Gb.conf_devices_idx_by_devicename[devicename] - else: - devicename = self.conf_device[CONF_IC3_DEVICENAME] + try: + if conf_device: + devicename = conf_device[CONF_IC3_DEVICENAME] + self.conf_device = conf_device + self.conf_device_idx = Gb.conf_devices_idx_by_devicename[devicename] + else: + devicename = self.conf_device[CONF_IC3_DEVICENAME] - event_msg = (f"Configuration Changed > DeleteDevice-{devicename}, " - f"{self.conf_device[CONF_FNAME]}/" - f"{DEVICE_TYPE_FNAME[self.conf_device[CONF_DEVICE_TYPE]]}") - post_event(event_msg) + event_msg = (f"Configuration Changed > DeleteDevice-{devicename}, " + f"{self.conf_device[CONF_FNAME]}/" + f"{DEVICE_TYPE_FNAME(self.conf_device[CONF_DEVICE_TYPE])}") + post_event(event_msg) - self._remove_device_tracker_entity(devicename) + # if deleting last device, use _delete all to simplifying table resetting + if len(Gb.conf_devices) <= 1: + return self._delete_all_devices() - self.dev_page_item[self.dev_page_no] = '' - Gb.conf_devices.pop(self.conf_device_idx) - self._update_config_file_tracking(update_config_flag=True) + self._remove_device_tracker_entity(devicename) - # The lists may have not been built if deleting a device when deleting an Apple acct - if self.device_items_by_devicename == {}: - return + self.dev_page_item[self.dev_page_no] = '' + Gb.conf_devices.pop(self.conf_device_idx) + self._update_config_file_tracking(update_config_flag=True) - del self.device_items_by_devicename[devicename] + # The lists may have not been built if deleting a device when deleting an Apple acct + if devicename in self.device_items_by_devicename: + del self.device_items_by_devicename[devicename] + # if self.device_items_by_devicename == {}: + # return + + # del self.device_items_by_devicename[devicename] + except Exception as err: + log_exception(err) #------------------------------------------------------------------------------------------- @@ -2893,26 +2928,41 @@ def _delete_all_devices(self): Delete the device_tracker entity and associated ic3 configuration """ - for conf_device in Gb.conf_devices: - devicename = conf_device[CONF_IC3_DEVICENAME] - self._remove_device_tracker_entity(devicename) + try: + for conf_device in Gb.conf_devices: + devicename = conf_device[CONF_IC3_DEVICENAME] + self._remove_device_tracker_entity(devicename) + + Gb.conf_devices = [] + self.device_items_by_devicename = {} # List of the apple_accts in the Gb.conf_tracking[apple_accts] parameter + self.device_items_displayed = [] # List of the apple_accts displayed on the device_list form + self.dev_page_item = ['', '', '', '', ''] # Device's devicename last displayed on each page + self.dev_page_no = 0 # apple_accts List form page number, starting with 0 - Gb.conf_devices = [] - self.devicename = {} - self.conf_device_idx = 0 - self.dev_page_item['', '', '', '', ''] + self._update_config_file_tracking(update_config_flag=True) + except Exception as err: + log_exception(err) + +#------------------------------------------------------------------------------------------- + def _reset_this_device_data_source_fields(self): + """ + Reset the iCloud & Mobile App to their initiial values. + Keep the devicename, friendly name, picture and other fields + """ + + self.conf_device.update(DEFAULT_DEVICE_DATA_SOURCE) self._update_config_file_tracking(update_config_flag=True) #------------------------------------------------------------------------------------------- - def _clear_icloud_mobapp_selection_parms(self): + def _reset_all_devices_data_source_fields(self): """ - Reset the iCloud & Mobile App, track_from_zone fields to their initiial values. + Reset the iCloud & Mobile App fields to their initiial values. Keep the devicename, friendly name, picture and other fields """ for conf_device in Gb.conf_devices: - conf_device.update(DEFAULT_DEVICE_REINITIALIZE_CONF) + conf_device.update(DEFAULT_DEVICE_DATA_SOURCE) self._update_config_file_tracking(update_config_flag=True) @@ -2939,7 +2989,7 @@ async def async_step_add_device(self, user_input=None, errors=None): user_input = self._strip_special_text_from_user_input(user_input, CONF_FNAME) user_input = self._strip_special_text_from_user_input(user_input, CONF_MOBILE_APP_DEVICE) user_input = self._option_text_to_parm(user_input, CONF_TRACKING_MODE, TRACKING_MODE_OPTIONS) - user_input = self._option_text_to_parm(user_input, CONF_DEVICE_TYPE, DEVICE_TYPE_FNAME) + user_input = self._option_text_to_parm(user_input, CONF_DEVICE_TYPE, DEVICE_TYPE_FNAMES) self.log_step_info(user_input, action_item) if (action_item == 'cancel' @@ -3045,11 +3095,13 @@ async def async_step_update_device(self, user_input=None, errors=None): else: user_input[CONF_APPLE_ACCOUNT] = self.conf_device[CONF_APPLE_ACCOUNT] user_input[CONF_FAMSHR_DEVICENAME] = self.conf_device[CONF_FAMSHR_DEVICENAME] - user_input[CONF_FMF_EMAIL] = 'None' + if CONF_FMF_EMAIL in user_input: + user_input[CONF_FMF_EMAIL] = 'None' + user_input[CONF_FMF_DEVICE_ID] = '' user_input = self._option_text_to_parm(user_input, CONF_MOBILE_APP_DEVICE, self.mobapp_list_text_by_entity_id) user_input = self._option_text_to_parm(user_input, CONF_PICTURE, self.picture_by_filename) - user_input = self._option_text_to_parm(user_input, CONF_DEVICE_TYPE, DEVICE_TYPE_FNAME) + user_input = self._option_text_to_parm(user_input, CONF_DEVICE_TYPE, DEVICE_TYPE_FNAMES) user_input = self._option_text_to_parm(user_input, CONF_TRACK_FROM_BASE_ZONE, self.zone_name_key_text) user_input = self._strip_special_text_from_user_input(user_input, CONF_IC3_DEVICENAME) self.log_step_info(user_input, action_item) @@ -3090,13 +3142,13 @@ async def async_step_update_device(self, user_input=None, errors=None): post_event( f"Configuration Changed > AddDevice-{ui_devicename}, " f"{self.conf_device[CONF_FNAME]}/" - f"{DEVICE_TYPE_FNAME[self.conf_device[CONF_DEVICE_TYPE]]}") + f"{DEVICE_TYPE_FNAME(self.conf_device[CONF_DEVICE_TYPE])}") else: Gb.conf_devices[self.conf_device_idx] = self.conf_device post_event (f"Configuration Changed > ChangeDevice-{ui_devicename}, " f"{self.conf_device[CONF_FNAME]}/" - f"{DEVICE_TYPE_FNAME[self.conf_device[CONF_DEVICE_TYPE]]}") + f"{DEVICE_TYPE_FNAME(self.conf_device[CONF_DEVICE_TYPE])}") self.dev_page_item[self.dev_page_no] = ui_devicename self._update_config_file_tracking(update_config_flag=True) @@ -3120,9 +3172,18 @@ async def async_step_update_device(self, user_input=None, errors=None): # Update the device_tracker & sensor entities now that the configuration has been updated if 'add_device' in self.conf_device_update_control: + # This is the first device being added, + # we need to set up the device_tracker platform, which will add it + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) if Gb.async_add_entities_device_tracker is None: await Gb.hass.config_entries.async_forward_entry_setups(Gb.config_entry, ['device_tracker']) - self._create_device_tracker_and_sensor_entities(ui_devicename, self.conf_device) + + sensors_list = self._build_all_sensors_list() + self._create_sensor_entity(devicename, conf_device, sensors_list) + + else: + self._create_device_tracker_and_sensor_entities(ui_devicename, self.conf_device) + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) else: self._update_changed_sensor_entities() @@ -3274,7 +3335,9 @@ def _validate_update_device(self, user_input): if user_input[CONF_FAMSHR_DEVICENAME].strip() == '': user_input[CONF_FAMSHR_DEVICENAME] = 'None' - user_input[CONF_FMF_EMAIL] = 'None' + if CONF_FMF_EMAIL in user_input: + user_input[CONF_FMF_EMAIL] = 'None' + user_input[CONF_FMF_DEVICE_ID] = '' if (user_input[CONF_MOBILE_APP_DEVICE].strip() == '' or user_input[CONF_MOBILE_APP_DEVICE] == 'scan_hdr'): @@ -3729,8 +3792,8 @@ async def _build_icloud_device_selection_list(self, selected_devicename=None): # Get the list of devices with valid apple accounts aa_idx = 0 - for apple_account in Gb.conf_apple_accounts: - username = apple_account[CONF_USERNAME] + for apple_acct in Gb.conf_apple_accounts: + username = apple_acct[CONF_USERNAME] aa_idx += 1 if Gb.username_valid_by_username.get(username, False) is False: @@ -3753,8 +3816,9 @@ async def _build_icloud_device_selection_list(self, selected_devicename=None): PyiCloud, PyiCloud.account_owner, selected_devicename) # Available devices - devices_cnt = len(devices_used) + len(devices_available) + len(this_device) + devices_cnt = len(devices_used) + len(devices_available) + len(this_device) assigned_cnt = len(devices_used) + len(this_device) + aa_idx_dots = '.'*aa_idx username_hdr_available = {f"{aa_idx_dots}hdr": f"🍎 ~~~~ {PyiCloud.account_owner}, " @@ -3804,57 +3868,64 @@ def _get_icloud_devices_list_avail_used_this( self, aa_idx, PyiCloud, apple_acct devices_assigned = {} selected_device_icloud_dname = '' for _conf_device in Gb.conf_devices: - if _conf_device[CONF_APPLE_ACCOUNT] != PyiCloud.username: + icloud_dname = _conf_device[CONF_FAMSHR_DEVICENAME] + username = _conf_device[CONF_APPLE_ACCOUNT] + if (icloud_dname == 'None' + or PyiCloud.username != username): continue - if _conf_device[CONF_FAMSHR_DEVICENAME] != 'None': - devices_assigned[_conf_device[CONF_FAMSHR_DEVICENAME]] = _conf_device[CONF_IC3_DEVICENAME] - devices_assigned[_conf_device[CONF_IC3_DEVICENAME]] = _conf_device[CONF_FAMSHR_DEVICENAME] + devices_assigned[icloud_dname] = _conf_device[CONF_IC3_DEVICENAME] + devices_assigned[_conf_device[CONF_IC3_DEVICENAME]] = icloud_dname - if _conf_device[CONF_FAMSHR_DEVICENAME] not in PyiCloud.device_id_by_icloud_dname: - icloud_dname_username = f"{_conf_device[CONF_FAMSHR_DEVICENAME]}{LINK}{PyiCloud.username}" - icloud_dname_owner = f"{_conf_device[CONF_FAMSHR_DEVICENAME]}{LINK}{PyiCloud.account_owner}{RLINK}" + if icloud_dname not in PyiCloud.device_id_by_icloud_dname: + icloud_dname_username = f"{icloud_dname}{LINK}{username}" + icloud_dname_owner = f"{icloud_dname}{LINK}{username}{RLINK}" unknown_devices[icloud_dname_username] = ( f"{RED_X}{icloud_dname_owner} >" f"{RARROW}UNKNOWN DEVICE") try: - for icloud_dname, device_display_name in PyiCloud.device_model_name_by_icloud_dname.items(): + for icloud_dname, device_model in PyiCloud.device_model_name_by_icloud_dname.items(): + device_id = PyiCloud.device_id_by_icloud_dname[icloud_dname] + _RawData = PyiCloud.RawData_by_device_id[device_id] + conf_apple_acct, conf_aa_idx = config_file.conf_apple_acct(PyiCloud.username) + locate_all_sym = '' if conf_apple_acct[CONF_LOCATE_ALL] else 'ⓧ ' + famshr_device = ' (FamShr)' if _RawData.family_share_device else '' + if famshr_device and locate_all_sym: + famshr_device = ' (FamShr - NOT LOCATING DEVICE)' + icloud_dname_username = f"{icloud_dname}{LINK}{PyiCloud.username}" icloud_dname_owner = f"{icloud_dname}{LINK}{PyiCloud.account_owner}{RLINK}" + icloud_dname_owner_model = f"{icloud_dname} > {device_model}{famshr_device}" # If not assigned to an ic3 device if icloud_dname not in devices_assigned: - device_id = PyiCloud.device_id_by_icloud_dname[icloud_dname] - _RawData = PyiCloud.RawData_by_device_id[device_id] - if _RawData.family_share_device: + if famshr_device: famshr_available[icloud_dname_username] = ( - f"{icloud_dname_owner} > " - f"{device_display_name}, " - f"Family Share Device" + f"{locate_all_sym}" + f"{icloud_dname_owner_model}" f"{aa_idx_dots}") else: owner_available[icloud_dname_username] = ( - f"{icloud_dname_owner} > " - f"{device_display_name}" + f"{icloud_dname_owner_model}" f"{aa_idx_dots}") continue # Is the icloud device name assigned to the current device being updated devicename = devices_assigned[icloud_dname] if devicename == selected_devicename: + err = RED_X if instr(icloud_dname_owner_model, 'NOT LOCATING') else '' this_device[icloud_dname_username] = ( - f"{icloud_dname_owner} > " - f"{device_display_name}") + f"{err}{icloud_dname_owner} > " + f"{device_model}{famshr_device}") continue # Assigned to another device - _assigned_to_fname = self._icloud_device_assigned_to(PyiCloud.username, icloud_dname) - if _assigned_to_fname: - devices_used[icloud_dname_username] = ( - f"{icloud_dname_owner}{RARROW}" - f"{_assigned_to_fname}, " - f"{device_display_name}") + _assigned_to_fname = self._icloud_device_assigned_to(PyiCloud, icloud_dname) + err = RED_X if instr(icloud_dname_owner_model, 'NOT LOCATING') else '' + devices_used[icloud_dname_username] = ( + f"{err}{icloud_dname_owner_model}{RARROW}" + f"{_assigned_to_fname}") except Exception as err: log_exception(err) @@ -3866,10 +3937,10 @@ def _get_icloud_devices_list_avail_used_this( self, aa_idx, PyiCloud, apple_acct return devices_available, devices_used, this_device #---------------------------------------------------------------------- - def _icloud_device_assigned_to(self, username, icloud_dname): - _assigned_to_fname = [f"{conf_device[CONF_FNAME]} ({conf_device[CONF_IC3_DEVICENAME]})" + def _icloud_device_assigned_to(self, PyiCloud, icloud_dname): + _assigned_to_fname = [f"{PyiCloud.account_owner_username})" for conf_device in Gb.conf_devices - if (username == conf_device[CONF_APPLE_ACCOUNT] + if (PyiCloud.username == conf_device[CONF_APPLE_ACCOUNT] and icloud_dname == conf_device[CONF_FAMSHR_DEVICENAME])] if _assigned_to_fname: @@ -4150,6 +4221,7 @@ def _create_device_tracker_and_sensor_entities(self, devicename, conf_device): NewDeviceTrackers = [] DeviceTracker = None + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) if devicename in Gb.DeviceTrackers_by_devicename: DeviceTracker = Gb.DeviceTrackers_by_devicename[devicename] else: @@ -4159,9 +4231,15 @@ def _create_device_tracker_and_sensor_entities(self, devicename, conf_device): if DeviceTracker is None: return + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) Gb.DeviceTrackers_by_devicename[devicename] = DeviceTracker NewDeviceTrackers.append(DeviceTracker) + # if devicename not in Gb.ha_device_id_by_devicename: + # NewDeviceTrackers.append(DeviceTracker) + # else: + # NewDeviceTrackers.append(DeviceTracker) + Gb.async_add_entities_device_tracker(NewDeviceTrackers, True) sensors_list = self._build_all_sensors_list() @@ -4178,6 +4256,7 @@ def _remove_device_tracker_entity(self, devicename): devicename to be removed """ # Inactive devices were not created so they are not in Gb.DeviceTrackers_by_devicename + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) if devicename not in Gb.DeviceTrackers_by_devicename: return @@ -4198,6 +4277,7 @@ def _remove_device_tracker_entity(self, devicename): DeviceTracker.remove_device_tracker() except: pass + ic3_device_tracker.get_ha_device_ids_from_device_registry(Gb.hass) #------------------------------------------------------------------------------------------- def _devices_form_identify_new_and_removed_tfz_zones(self, user_input): @@ -4554,7 +4634,7 @@ def _parm_or_device(self, pname, suggested_value=''): or suggested_value if pname == 'device_type': - parm_displayed = DEVICE_TYPE_FNAME.get(parm_displayed, IPHONE_FNAME) + parm_displayed = DEVICE_TYPE_FNAME(parm_displayed) parm_displayed = ' ' if parm_displayed == '' else parm_displayed except Exception as err: @@ -4772,7 +4852,7 @@ def _extract_name_device_type(self, devicename): elif instr(devicename, ic3dev_type): fnamew = devicename.replace(ic3dev_type, "") fname = fnamew.replace("_", "").replace("-", "").title().strip() - device_type = DEVICE_TYPE_FNAME.get(ic3dev_type, ic3dev_type) + device_type = DEVICE_TYPE_FNAME(ic3dev_type) break if device_type == "": diff --git a/custom_components/icloud3/config_flow_forms.py b/custom_components/icloud3/config_flow_forms.py index bada6c1..3e10178 100644 --- a/custom_components/icloud3/config_flow_forms.py +++ b/custom_components/icloud3/config_flow_forms.py @@ -10,7 +10,7 @@ from .global_variables import GlobalVariables as Gb from .const import (RED_ALERT, LINK, RLINK, RARROW, IPHONE, IPAD, WATCH, AIRPODS, ICLOUD, OTHER, - DEVICE_TYPE_FNAME, MOBAPP, NO_MOBAPP, + DEVICE_TYPE_FNAME, DEVICE_TYPE_FNAMES, MOBAPP, NO_MOBAPP, INACTIVE_DEVICE, HOME_DISTANCE, PICTURE_WWW_STANDARD_DIRS, CONF_PICTURE_WWW_DIRS, DEFAULT_DEVICE_CONF, @@ -313,7 +313,7 @@ def form_update_apple_acct(self): default=password): password_selector, vol.Optional(CONF_TOTP_KEY, - default=totp_key): + default='For future use in supporting hardware keys (YubiKey)'): selector.TextSelector(), vol.Optional('locate_all', default=locate_all): @@ -498,6 +498,7 @@ def _build_device_items_displayed_over_5(self): #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def form_add_device(self): self.actions_list = ACTION_LIST_ITEMS_BASE.copy() + device_type_fname = DEVICE_TYPE_FNAME(self._parm_or_device(CONF_DEVICE_TYPE)) return vol.Schema({ vol.Required(CONF_IC3_DEVICENAME, @@ -509,7 +510,7 @@ def form_add_device(self): vol.Required(CONF_DEVICE_TYPE, default=self._parm_or_device(CONF_DEVICE_TYPE, suggested_value=IPHONE)): selector.SelectSelector(selector.SelectSelectorConfig( - options=dict_value_to_list(DEVICE_TYPE_FNAME), mode='dropdown')), + options=dict_value_to_list(DEVICE_TYPE_FNAMES), mode='dropdown')), vol.Required(CONF_TRACKING_MODE, default=self._option_parm_to_text(CONF_TRACKING_MODE, TRACKING_MODE_OPTIONS)): selector.SelectSelector(selector.SelectSelectorConfig( @@ -536,7 +537,8 @@ def form_update_device(self): # Display Advanced Tracking Parameters log_zones_fnames = [zone_dname(zone) for zone in self.conf_device[CONF_LOG_ZONES] if zone.startswith('Name') is False] tfz_fnames = [zone_dname(zone) for zone in self.conf_device[CONF_TRACK_FROM_ZONES]] - RARELY_UPDATED_PARMS_HEADER = ( f"DeviceType ({self._option_parm_to_text(CONF_DEVICE_TYPE, DEVICE_TYPE_FNAME)}), " + device_type_fname = DEVICE_TYPE_FNAME(self._parm_or_device(CONF_DEVICE_TYPE)) + RARELY_UPDATED_PARMS_HEADER = ( f"DeviceType ({device_type_fname}), " f"inZoneInterval ({format_timer(self.conf_device[CONF_INZONE_INTERVAL]*60)}), " f"FixedInterval ({format_timer(self.conf_device[CONF_FIXED_INTERVAL]*60)}), " f"LogFromZones ({list_to_str(log_zones_fnames)}), " @@ -628,11 +630,12 @@ def form_update_device(self): }) if self.display_rarely_updated_parms: + device_type_fname = DEVICE_TYPE_FNAME(self._parm_or_device(CONF_DEVICE_TYPE)) schema.update({ vol.Required(CONF_DEVICE_TYPE, - default=self._option_parm_to_text(CONF_DEVICE_TYPE, DEVICE_TYPE_FNAME)): + default=self._option_parm_to_text(CONF_DEVICE_TYPE, DEVICE_TYPE_FNAMES)): selector.SelectSelector(selector.SelectSelectorConfig( - options=dict_value_to_list(DEVICE_TYPE_FNAME), mode='dropdown')), + options=dict_value_to_list(DEVICE_TYPE_FNAMES), mode='dropdown')), vol.Required(CONF_INZONE_INTERVAL, default=self.conf_device[CONF_INZONE_INTERVAL]): # default=self._parm_or_device(CONF_INZONE_INTERVAL)): diff --git a/custom_components/icloud3/const.py b/custom_components/icloud3/const.py index 15127e3..c6a7166 100644 --- a/custom_components/icloud3/const.py +++ b/custom_components/icloud3/const.py @@ -10,7 +10,7 @@ # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -VERSION = '3.1' +VERSION = '3.1.1' VERSION_BETA = '' #----------------------------------------- ICLOUD3 = 'iCloud3' @@ -121,7 +121,7 @@ IPHONE_FNAME, IPAD_FNAME, WATCH_FNAME, AIRPODS_FNAME, IMAC_FNAME, IPOD_FNAME, ICLOUD, ] -DEVICE_TYPE_FNAME = { +DEVICE_TYPE_FNAMES = { IPHONE: IPHONE_FNAME, IPAD: IPAD_FNAME, WATCH: WATCH_FNAME, @@ -130,6 +130,9 @@ IPOD: IPOD_FNAME, OTHER: OTHER_FNAME, } +def DEVICE_TYPE_FNAME(device_type): + return DEVICE_TYPE_FNAMES.get(device_type, device_type) + DEVICE_TYPE_ICONS = { IPHONE: "mdi:cellphone", IPAD: "mdi:tablet", @@ -315,12 +318,12 @@ NL_DOT = f'{NL} • ' CRLF_XD = f'{CRLF}{NBSP2}×{NBSP2}' CRLF_X = f'{CRLF}{NBSP3}×{NBSP2}' -CRLF_HDOT = f'{CRLF}{NBSP6}◦{NBSP2}' -CRLF_CHK = f'{CRLF}{NBSP3}✓{NBSP}' -CRLF_STAR = f'{CRLF}{NBSP3}✪{NBSP}' +CRLF_CIRCLE_X = f'{CRLF}{NBSP2}⮾{NBSP}' CRLF_RED_X = f'{CRLF}❌' +CRLF_HDOT = f'{CRLF}{NBSP4}{NBSP3}◦{NBSP2}' +CRLF_CHK = f'{CRLF}{NBSP3}✓{NBSP}' +CRLF_STAR = f'{CRLF}{NBSP2}✪{NBSP}' CRLF_YELLOW_ALERT = f'{CRLF}⚠️{NBSP}' -CRLF_CIRCLE_X = f'{CRLF}{NBSP2}ⓧ{NBSP}' CRLF_SP3_DOT = f'{CRLF}{NBSP3}•{NBSP}' CRLF_SP5_DOT = f'{CRLF}{NBSP5}•{NBSP}' CRLF_SP8_DOT = f'{CRLF}{NBSP4}{NBSP4}•{NBSP}' @@ -861,7 +864,7 @@ CONF_IC3_DEVICENAME: ' ', CONF_FNAME: '', CONF_PICTURE: 'None', - CONF_EVLOG_DISPLAY_ORDER: 0, + #CONF_EVLOG_DISPLAY_ORDER: 0, CONF_UNIQUE_ID: '', CONF_DEVICE_TYPE: 'iPhone', CONF_INZONE_INTERVAL: 120, @@ -873,29 +876,29 @@ CONF_RAW_MODEL : '', CONF_MODEL: '', CONF_MODEL_DISPLAY_NAME: '', - CONF_FMF_EMAIL: 'None', - CONF_FMF_DEVICE_ID: '', + #CONF_FMF_EMAIL: 'None', + #CONF_FMF_DEVICE_ID: '', CONF_MOBILE_APP_DEVICE: 'None', CONF_TRACK_FROM_BASE_ZONE: HOME, CONF_TRACK_FROM_ZONES: [HOME], CONF_LOG_ZONES: ['none'], } +# Used in conf_flow to reinialize the Configuration Devices +DEFAULT_DEVICE_DATA_SOURCE = { + CONF_APPLE_ACCOUNT: '', + CONF_FAMSHR_DEVICENAME: 'None', + CONF_FAMSHR_DEVICE_ID: '', + CONF_RAW_MODEL : '', + CONF_MODEL: '', + CONF_MODEL_DISPLAY_NAME: '', + CONF_MOBILE_APP_DEVICE: 'None', +} RANGE_DEVICE_CONF = { CONF_INZONE_INTERVAL: [5, 480], CONF_FIXED_INTERVAL: [0, 480], } -# Used in conf_flow to reinialize the Configuration Devices -# Reset the FamShe FmF Mobile App track_from_zone fields -DEFAULT_DEVICE_REINITIALIZE_CONF = DEFAULT_DEVICE_CONF.copy() -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_IC3_DEVICENAME, None) -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_FNAME, None) -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_PICTURE, None) -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_EVLOG_DISPLAY_ORDER, None) -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_DEVICE_TYPE, None) -DEFAULT_DEVICE_REINITIALIZE_CONF.pop(CONF_UNIQUE_ID, None) - DEFAULT_GENERAL_CONF = { CONF_LOG_LEVEL: 'debug-auto-reset', CONF_LOG_LEVEL_DEVICES: ['all'], diff --git a/custom_components/icloud3/const_config_flow.py b/custom_components/icloud3/const_config_flow.py index 2fc0b8a..0c8c1a0 100644 --- a/custom_components/icloud3/const_config_flow.py +++ b/custom_components/icloud3/const_config_flow.py @@ -73,12 +73,13 @@ 'update_device': 'SELECT THE DEVICE ᐳ Update the selected device, Add a new device to be tracked by iCloud3, Display more Devices on the next page', 'add_device': 'ADD DEVICE ᐳ Add a device to be tracked by iCloud3', - 'delete_device': 'DELETE DEVICE(S), OTHER DEVICE MAINTENANCE ᐳ Delete the device(s) from the tracked device list, clear the iCloud/Mobile App selection fields', + 'delete_device': 'TOOLS - RESET DATA SOURCE(S), DELETE DEVICE(S) ᐳ Reset Apple Acct & Mobile App to `None`, Delete the device(s)', 'change_device_order': 'CHANGE DEVICE ORDER ᐳ Change the tracking order of the Devices and their display sequence on the Event Log', - 'delete_this_device': 'DELETE THIS DEVICE ᐳ Delete this device', - 'delete_all_devices': 'DELETE ALL DEVICES ᐳ Delete all devices from the iCloud3 tracked devices list', - 'delete_icloud_mobapp_info':'CLEAR ICLOUDR/MOBAPP INFO ᐳ Reset the iCloud/Mobile App seletion fields on all devices', + 'reset_this_device_data_source': 'RESET THIS DEVICE`S DATA SOURCE ᐳ Set Apple Acct & Mobile App to `None`', + 'delete_this_device': 'DELETE THIS DEVICE ᐳ Delete this device from the iCloud3 tracked devices list', + 'reset_all_devices_data_source': '⚠️ RESET ALL DEVICE`S DATA SOURCE ᐳ Set Apple Acct & Mobile App to `None`', + 'delete_all_devices': '⚠️ DELETE ALL DEVICES ᐳ Delete all devices from the iCloud3 tracked devices list', 'delete_device_cancel': 'CANCEL ᐳ Return to the Device List screen', 'inactive_to_track': 'TRACK ALL OR SELECTED ᐳ Change the `Tracking Mode‘ of all of the devices (or the selected devices) from `Inactive‘ to `Tracked‘', @@ -171,9 +172,10 @@ ACTION_LIST_OPTIONS['change_device_order'], ACTION_LIST_OPTIONS['return']] DELETE_DEVICE_ACTIONS = [ + ACTION_LIST_OPTIONS['reset_this_device_data_source'], ACTION_LIST_OPTIONS['delete_this_device'], + ACTION_LIST_OPTIONS['reset_all_devices_data_source'], ACTION_LIST_OPTIONS['delete_all_devices'], - ACTION_LIST_OPTIONS['delete_icloud_mobapp_info'], ACTION_LIST_OPTIONS['delete_device_cancel']] REVIEW_INACTIVE_DEVICES = [ ACTION_LIST_OPTIONS['inactive_to_track'], diff --git a/custom_components/icloud3/device.py b/custom_components/icloud3/device.py index 99bf8fc..fb72423 100644 --- a/custom_components/icloud3/device.py +++ b/custom_components/icloud3/device.py @@ -124,8 +124,8 @@ def initialize(self): # Operational variables self.device_type = 'iPhone' - self.raw_model = DEVICE_TYPE_FNAME.get(self.device_type, self.device_type) # iPhone15,2 - self.model = DEVICE_TYPE_FNAME.get(self.device_type, self.device_type) # iPhone + self.raw_model = DEVICE_TYPE_FNAME(self.device_type) # iPhone15,2 + self.model = DEVICE_TYPE_FNAME(self.device_type) # iPhone #self.model_display_name = DEVICE_TYPE_FNAME.get(self.device_type, self.device_type) # iPhone 14 Pro self.model_display_name = Gb.model_display_name_by_raw_model.get(self.raw_model, self.raw_model) # iPhone 14 Pro self.data_source = None @@ -815,15 +815,15 @@ def devicename_fname(self): @property def devtype_fname(self): - return DEVICE_TYPE_FNAME.get(self.device_type, self.device_type) + return DEVICE_TYPE_FNAME(self.device_type) @property def fname_devtype(self): - if instr(self.fname, DEVICE_TYPE_FNAME.get(self.device_type, self.device_type)): + if instr(self.fname, DEVICE_TYPE_FNAME(self.device_type)): return self.fname return (f"{self.fname} " - f"({DEVICE_TYPE_FNAME.get(self.device_type, self.device_type)})") + f"({DEVICE_TYPE_FNAME(self.device_type)})") # return (f"{self.fname}{INFO_SEPARATOR}" # f"{DEVICE_TYPE_FNAME.get(self.device_type, self.device_type)}") diff --git a/custom_components/icloud3/device_tracker.py b/custom_components/icloud3/device_tracker.py index 6e33d6f..ea6a0f3 100644 --- a/custom_components/icloud3/device_tracker.py +++ b/custom_components/icloud3/device_tracker.py @@ -32,7 +32,7 @@ log_info_msg, log_debug_msg, log_error_msg, log_exception, log_exception_HA, log_info_msg_HA, _evlog, _log, ) -from .helpers.time_util import (adjust_time_hour_values, secs_to_datetime) +from .helpers.time_util import (adjust_time_hour_values, datetime_now, ) from .support import start_ic3 from .support import config_file @@ -73,7 +73,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e for conf_device in Gb.conf_devices if conf_device[CONF_IC3_DEVICENAME] != ''] - _get_ha_device_ids_from_device_registry(hass) + get_ha_device_ids_from_device_registry(hass) except Exception as err: log_exception(err) @@ -93,8 +93,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e if DeviceTracker: Gb.DeviceTrackers_by_devicename[devicename] = DeviceTracker + # if devicename not in Gb.ha_device_id_by_devicename: + # NewDeviceTrackers.append(DeviceTracker) + # else: + # NewDeviceTrackers.append(DeviceTracker) NewDeviceTrackers.append(DeviceTracker) + # Set the total count of the device_trackers that will be created if Gb.device_trackers_cnt == 0: Gb.device_trackers_cnt = len(NewDeviceTrackers) @@ -103,7 +108,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e if NewDeviceTrackers != []: async_add_entities(NewDeviceTrackers, True) - _get_ha_device_ids_from_device_registry(hass) + get_ha_device_ids_from_device_registry(hass) log_info_msg_HA(f"{ICLOUD3} Device Tracker entities: {Gb.device_trackers_cnt}") Devices_no_area = [Device for Device in Gb.DeviceTrackers_by_devicename.values() \ @@ -123,7 +128,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e log_error_msg(log_msg) #------------------------------------------------------------------------------------------- -def _get_ha_device_ids_from_device_registry(hass): +def get_ha_device_ids_from_device_registry(hass): ''' Cycle thru the ha device registry, extract the iCloud3 entries and associate the ha device_id with the ic3_devicename parameters @@ -196,6 +201,12 @@ def _get_ha_device_id_from_device_entry(hass, device, device_entry): if item in Gb.conf_devicenames: Gb.ha_device_id_by_devicename[item] = device_entry.id Gb.ha_area_id_by_devicename[item] = device_entry.area_id + # _log(f"{device_entry.id=}") + # _log(f"{device_entry.name=}") + # _log(f"{device_entry.name_by_user=}") + # _log(f"{device_entry.identifiers=}") + # _log(f"{device_entry.is_new=}") + # _log(f"{device_entry.disabled_by=}") except Exception as err: @@ -215,10 +226,8 @@ def __init__(self, devicename, conf_device, data=None): self.devicename = devicename self.Device = None # Filled in after Device object has been created in start_ic3 self.entity_id = f"device_tracker.{devicename}" - # If the DOMAIN is not 'icloud3' ('iCloud3_dev'), add it to the device_tracker - # entity name to make it unique - if DOMAIN != 'icloud3': self.entity_id += f"_{DOMAIN}" self.ha_device_id = Gb.ha_device_id_by_devicename.get(self.devicename) + self.ha_area_id = Gb.ha_area_id_by_devicename.get(self.devicename) self.device_fname = conf_device[FNAME] @@ -241,6 +250,7 @@ def __init__(self, devicename, conf_device, data=None): self._attr_force_update = True self._unsub_dispatcher = None self._on_remove = [self.after_removal_cleanup] + self.remove_device_flag = False self.entity_removed_flag = False self.extra_attrs_track_from_zones = 'home' @@ -483,30 +493,32 @@ def _get_attribute_value(self, attribute): return attr_value #------------------------------------------------------------------------------------------- - def update_entity_attribute(self, new_fname=None, area_id=None): + def update_entity_attribute(self, new_fname=None, area_id=None, removing_device=False): """ Update entity definition attributes """ ha_device_id = Gb.ha_device_id_by_devicename.get(self.devicename) if ha_device_id is None: return - if new_fname is None and area_id is None: + if removing_device: + pass + elif new_fname is None and area_id is None: return - try: - area_id = area_id or self.ha_area_id or Gb.area_id_personal_device - area_reg = ar.async_get(Gb.hass) - area_name = area_reg.async_get_area(area_id).name - except: - area_id = area_name = None + # try: + # area_id = area_id or self.ha_area_id or Gb.area_id_personal_device + # area_reg = ar.async_get(Gb.hass) + # area_name = area_reg.async_get_area(area_id).name + # except: + # area_id = area_name = None self.device_fname = new_fname or self.device_fname - self.ha_area_id = Gb.ha_area_id_by_devicename[self.devicename] = \ - area_id + # self.ha_area_id = Gb.ha_area_id_by_devicename[self.devicename] = \ + # area_id log_debug_msg(f"Device Tracker entity changed: device_tracker.{self.devicename}, " - f"{self.device_fname} " - f"({area_name})") + f"{self.device_fname}") + # f"({area_name})") kwargs = {} kwargs['original_name'] = self.device_fname @@ -540,7 +552,7 @@ def update_entity_attribute(self, new_fname=None, area_id=None): kwargs = {} kwargs['name'] = f"{self.device_fname} ({self.devicename})" kwargs['name_by_user'] = "" - kwargs['area_id'] = self.ha_area_id + # kwargs['area_id'] = self.ha_area_id device_registry = dr.async_get(Gb.hass) dr_entry = device_registry.async_update_device(self.ha_device_id, **kwargs) @@ -582,6 +594,16 @@ def _remove_from_registries(self) -> None: if not self.registry_entry: return + device_registry = dr.async_get(Gb.hass) + + self.remove_device_flag = True + + # deleted = f"deleted-{datetime_now()}" + # kwargs = {'merge_identifiers': (deleted)} + # # kwargs = {'merge_identifiers': (DOMAIN, self.devicename, deleted)} + # dr_entry = device_registry.async_update_device(self.ha_device_id, **kwargs) + # _log(f"{dr_entry=}") + # Remove from device registry. if device_id := self.registry_entry.device_id: device_registry = dr.async_get(self.hass) diff --git a/custom_components/icloud3/global_variables.py b/custom_components/icloud3/global_variables.py index b9ebeae..cbe14fb 100644 --- a/custom_components/icloud3/global_variables.py +++ b/custom_components/icloud3/global_variables.py @@ -94,6 +94,7 @@ class GlobalVariables(object): HALogger = None iC3Logger = None iC3Logger_last_check_exist_secs = 0 + prestartup_log = '' iC3EntityPlatform = None # iCloud3 Entity Platform (homeassistant.helpers.entity_component) PyiCloud = None # iCloud Account service diff --git a/custom_components/icloud3/helpers/messaging.py b/custom_components/icloud3/helpers/messaging.py index 63bba89..f894d4f 100644 --- a/custom_components/icloud3/helpers/messaging.py +++ b/custom_components/icloud3/helpers/messaging.py @@ -25,21 +25,21 @@ INFO, GPS_ACCURACY, GPS, POLL_COUNT, VERT_ACCURACY, ALTITUDE, BADGE, ) -from ..const_more_info import more_info_text -from .common import (obscure_field, instr, is_empty, isnot_empty, ) +from ..const_more_info import more_info_text +from .common import (obscure_field, instr, is_empty, isnot_empty, ) import homeassistant.util.dt as dt_util from homeassistant.components import persistent_notification import os import time -from inspect import getframeinfo, stack +from inspect import getframeinfo, stack import traceback import logging DO_NOT_SHRINK = ['url', 'accountName', ] FILTER_DATA_DICTS = ['items', 'userInfo', 'dsid', 'dsInfo', 'webservices', 'locations','location', - 'params', 'headers', 'kwargs', ] + 'params', 'headers', 'kwargs', 'clientContext', ] FILTER_DATA_LISTS = ['devices', 'content', 'followers', 'following', 'contactDetails',] FILTER_FIELDS = [ ICLOUD3_VERSION, AUTHENTICATED, @@ -71,7 +71,15 @@ 'dsWebAuthToken', 'accountCountryCode', 'extended_login', 'trustToken', 'data', 'json', 'headers', 'params', 'url', 'retry_cnt', 'retried', 'retry', '#', 'code', 'ok', 'method', 'securityCode', + 'fmly', 'shouldLocate', 'selectedDevice', 'accountName', 'salt', 'a', 'b', 'c', 'm1', 'm2', 'protocols', 'iteration', 'Authorization', ] +FILTER_OUT = [ + 'features', 'BTR', 'LLC', 'CLK', 'TEU', 'SND', 'ALS', 'CLT', 'PRM', 'SVP', 'SPN', 'XRM', 'NWF', 'CWP', + 'MSG', 'LOC', 'LME', 'LMG', 'LYU', 'LKL', 'LST', 'LKM', 'WMG', 'SCA', 'PSS', 'EAL', 'LAE', 'PIN', + 'LCK', 'REM', 'MCS', 'REP', 'KEY', 'KPD', 'WIP', 'scd', + 'rm2State', 'pendingRemoveUntilTS', 'repairReadyExpireTS', 'repairReady', 'lostModeCapable', 'wipedTimestamp', + 'encodedDeviceId', 'scdPh', 'locationCapable', 'trackingInfo', 'nwd', 'remoteWipe', 'canWipeAfterLock', 'baUUID', + 'snd', 'continueButtonTitle', 'alertText', 'cancelButtonTitle', 'createTimestamp', 'alertTitle', ] SP_str = ' '*50 @@ -298,6 +306,10 @@ def open_ic3log_file_init(): #------------------------------------------------------------------------------ def open_ic3log_file(new_log_file=False): + # Items will be logged to home-assistane.log until the configuration file has been read + if is_empty(Gb.conf_general): + return + ic3logger_file = Gb.hass.config.path(IC3LOG_FILENAME) filemode = 'w' if new_log_file else 'a' @@ -312,6 +324,7 @@ def open_ic3log_file(new_log_file=False): write_config_file_to_ic3log() + #-------------------------------------------------------------------- def write_ic3log_recd(log_msg): ''' @@ -320,6 +333,10 @@ def write_ic3log_recd(log_msg): and renames while iCloud3 is running ''' try: + if is_empty(Gb.conf_general): + Gb.prestartup_log += f"\n{dt_util.now().strftime(DATETIME_FORMAT)[5:19]} {log_msg}" + return + time_now_msecs = time.time() if time_now_msecs - Gb.iC3Logger_last_check_exist_secs > 2: Gb.iC3Logger_last_check_exist_secs = time_now_msecs @@ -330,6 +347,7 @@ def write_ic3log_recd(log_msg): Gb.iC3Logger.info(log_msg) else: open_ic3log_file(new_log_file=True) + Gb.iC3Logger.info(log_msg) except Exception as err: @@ -393,6 +411,10 @@ def archive_ic3log_file(): #------------------------------------------------------------------------------ def write_config_file_to_ic3log(): + if Gb.prestartup_log: + write_ic3log_recd(f"{Gb.prestartup_log}") + Gb.prestartup_log = '' + conf_tracking_recd = Gb.conf_tracking.copy() # conf_tracking_recd[CONF_USERNAME] = obscure_field(conf_tracking_recd[CONF_USERNAME]) conf_tracking_recd[CONF_PASSWORD] = obscure_field(conf_tracking_recd[CONF_PASSWORD]) @@ -949,6 +971,8 @@ def post_internal_error(err_text, traceback_format_exec_obj='+'): #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def dummy_evlog(): _evlog(None, None) +def dummy_log(): + _log(None, None) #-------------------------------------------------------------------- def _evlog(devicename_or_Device, items='+'): @@ -985,7 +1009,8 @@ def _log(items, v1='+++', v2='', v3='', v4='', v5=''): except Exception as err: - log_exception(err) + Gb.HARootLogger.info(trace_msg) + # log_exception(err) #-------------------------------------------------------------------- def _called_from(trace=False): diff --git a/custom_components/icloud3/icloud3_main.py b/custom_components/icloud3/icloud3_main.py index d4f0384..75203ab 100644 --- a/custom_components/icloud3/icloud3_main.py +++ b/custom_components/icloud3/icloud3_main.py @@ -143,13 +143,6 @@ def start_icloud3(self): Gb.start_icloud3_inprocess_flag = True Gb.restart_icloud3_request_flag = False Gb.all_tracking_paused_flag = False - # _evlog(f"fil exists {Gb.icloud3_config_filename} {file_exists(Gb.icloud3_config_filename)}") - # _evlog(f"dir exists {Gb.ha_storage_icloud3} {directory_exists(Gb.ha_storage_icloud3)}") - # _evlog(f"dir exists {Gb.ha_storage_directory} {directory_exists(Gb.ha_storage_directory)}") - # _evlog(f"fil exists {f'{Gb.icloud3_config_filename}.test2'} {file_exists(f'{Gb.icloud3_config_filename}.test2')}") - # _evlog(f"dir exists {f'{Gb.ha_storage_icloud3}/test2'} {directory_exists(f'{Gb.ha_storage_icloud3}/test2')}") - # _evlog(f"make dir {f'make-{Gb.ha_storage_icloud3}/test2'} {make_directory(f'{Gb.ha_storage_icloud3}/test2')}") - # _evlog(f"ext filename {Gb.icloud3_config_filename} {extract_filename(Gb.icloud3_config_filename)}") start_ic3_control.stage_1_setup_variables() start_ic3_control.stage_2_prepare_configuration() diff --git a/custom_components/icloud3/manifest-dev.json b/custom_components/icloud3/manifest-dev.json deleted file mode 100644 index 00888bb..0000000 --- a/custom_components/icloud3/manifest-dev.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "domain": "icloud3", - "name": "iCloud3 v3", - "after_dependencies": ["recorder", "ios"], - "codeowners": ["@gcobb321"], - "config_flow": true, - "dependencies": [], - "documentation": "https://gcobb321.github.io/icloud3_v3_docs/#/", - "iot_class": "cloud_polling", - "issue_tracker": "https://github.com/gcobb321/icloud3_v3/issues", - "loggers": ["icloud3"], - "requirements": ["srp"], - "version": "3.1" -} diff --git a/custom_components/icloud3/strings - Copy.json b/custom_components/icloud3/strings - Copy.json new file mode 100644 index 0000000..60d718c --- /dev/null +++ b/custom_components/icloud3/strings - Copy.json @@ -0,0 +1,554 @@ +{ + "title": "iCloud3 v3: iDevice Tracker", + "config": { + "abort": { + "config_update_complete": "iCloud3 configuration updated successfully", + "already_configured": "iCloud3 is already installed and can not be installed again. 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", + "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_accepted": "The Apple ID Verification Code was accepted. Reauthentication is complete", + "verification_code_cancelled": "The Apple ID Verification was cancelled", + "update_cancelled": "Update Cancelled", + "icloud3_init_error": "A problem was encountered initializing the iCloud3 Configure Settings screens. iCloud3 has probably encountered an error during initialization and was not started. Check the 'home-assistant.log' file for any errors related to iCloud3", + "reauth_apple_acct_unknown": "Apple account requesting verification code is unknown", + "reauth_apple_acct_unused": "Apple account requesting verification code is not used" + }, + "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_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", + "verification_code_cancelled": "The Apple ID Verification was cancelled", + + "icloud_no_devices": "No devices were found in the iCloud `Family Sharing` list", + "icloud_other_error": "An unknown error was encountered authenticating the Apple Account. Try again later" + }, + "step": { + "user": { + "data": { + "continue": "Select to continue iCloud3 installation (and parameter migration)", + "continue_restart_ha": "Select to continue iCloud3 installation. Then RESTART HOME ASSISTANT." + }, + "description": "CONGRATULATIONS! The iCloud3 v3 Integration has been added to Home Assistant. The next step is to configure the devices you want to track. If you are running iCloud3 v2, your configuration will be migrated from v2 to v3\n\nSelect SUBMIT below, then select CONFIGURE to configure iCloud3.\n\n\n-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --\nThe iCloud3 User Guide will help you get started:\n• ICLOUD3 DOCUMENTATION - Select the question mark (?) in the upper-right\n  corner of this screen to open the “iCloud User Guide”\n• MIGRATING FROM v2 - Follow the steps in the “Installing iCloud3 > Migrating\n  from v2 to v3” chapter\n• NEW INSTALLATION - Follow the steps in the “Installing iCloud3 > As a new\n  Installation” chapter\n-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --", + "title": "iCloud3 v3 Integration Installer" + }, + "reauth": { + "title": "Apple ID Verification Code (HA Notifications)", + "description": "Enter the 6-digit verification code you just received from Apple", + "data": { + "apple_account": "APPLE ACCOUNT TO BE AUTHENTICATED", + "verification_code": "APPLE ACCOUNT VERIFICATION CODE", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "restart_ha": { + "title": "Confirm Restarting Home Assistant", + "description": "iCloud3 needs to restart Home Assistant to prevent device_tracker entity name conflicts", + "data": { + "action_items": "" + } + } + } + }, + "options": { + "abort": { + "config_update_complete": "iCloud3 configuration updated successfully", + "already_configured": "iCloud3 is already installed and can not be installed again. Select CONFIGURE in the iCloud3 Integration entry to configure iCloud3", + "disabled": "iCloud3 is DISABLED and can not be installed again. Enable iCloud, then select CONFIGURE in the iCloud3 Integration entry to configure iCloud3", + "ha_restarting": "Home Assistant is Restarting\n\nTHE ICLOUD3 SCREEN MUST BE REFRESHED AFTER RESTARTING", + "ic3_restarting": "Restarting iCloud3", + "reauth_successful": "The reauthentication has been successfully completed" + }, + "error": { + "update_aborted": "Update aborted, an error was detected in one of the data fields", + "conf_updated": "✅ iCloud3 Configuration Parameters were updated successfully", + "conf_reloaded": "iCloud3 Configuration File was Reloaded", + "icloud_acct_logging_into": "Logging into Apple Account", + "icloud_acct_logged_into": "✅ Logged into the Apple Account", + "icloud_acct_already_logged_into": "Already Logged into the Apple Account", + "icloud_acct_locate_all_reqd": "All devices must be located. Other Family devices are tracked with this Apple Account", + "icloud_acct_login_error_user_pw": "❌ Login Error, Invalid Username or Password", + "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_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_username_password_error": "❌ Entry Error, Invalid Username or Password", + "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_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_not_set_up": "Apple Account Username or Password needs to be entered", + "icloud_acct_no_data_source": "❌ No Data Source has been selected (Apple iCloud 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", + + "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_needed": "The Apple Account Verification Code is needed", + "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", + "verification_code_send_error": "❌ Failed to send the Apple ID Verification Code", + + "inactive_device": "Device is INACTIVE. Change to `Track` to locate and track this device", + "inactive_all_devices": "✪✪ ALL DEVICES ARE INACTIVE. NOTHING WILL BE TRACKED ✪✪", + "inactive_most_devices": "MOST Devices are INACTIVE and will not be located or tracked", + "inactive_some_devices": "Some Devices are INACTIVE and will not be located or tracked", + "inactive_few_devices": "A Few Devices are INACTIVE and will not be located or tracked", + "inactive_no_devices": "No Devices have been set up. On the `Update Devices` screen, select `Add a New Device`, then select `Submit` to display the screen to add an iCloud3 device_tracker entity.", + + "away_time_zone_dup_devices_1": "One of these devices is also selected in the Other Device List", + "away_time_zone_dup_devices_2": "One of these devices is also selected in the Otner Device List", + + "review_filledin_fields": "Review the 'Filled in' fields", + "not_numeric": "❌ The value entered is not numeric", + "waze_server_error_us": "The correct server for your location is: United States, Canada", + "waze_server_error_il": "The correct server for your location is: Israel", + "waze_server_error_row": "The correct server for your location is: Rest of the World", + "required_field": "❌ This parameter must be specified", + "required_field_device": "A device providing location data must be selected from the Family Share, Find-my-Friends, or Mobile App devices lists", + "no_device_selected": "❌ A device providing location data must be selected", + "no_add_entities_device_tracker_fct": "The HA component for adding devices is not available. HA MUST BE RESTARTED", + + "unknown_value": "One of the selection parameters needs to be reviewed", + "unknown_devicename": "The configured device was not found in any of the Apple Accts or the Mobile App device list", + "unknown_icloud": "The configured device was not found in any of the Apple Accounts", + "unknown_mobapp": "The configured device was not found in the Mobile App devices list", + "unknown_picture": "The configured picture file was not found in `config/www/...` directories", + + "unknown_apple_acct": "Apple Account is not selected for the assigned iCloud device", + "unknown_icloud_mobapp": "Check the iCloud and Mobile App parameter values (Not found or Invalid)", + "unknown_icloud_mobapp_picture": "Check the iCloud, Mobile App and Picture parameter values (Not found or Invalid)", + "unknown_icloud_picture": "Check the iCloud and Picture parameter values (Not found or Invalid)", + + "tfz_selection_invalid": "The value must be a zone that is being tracked from", + "time_factor_invalid_range": "The 'Travel Time Multiplier' must be between .1 and .9", + "fixed_interval_invalid_range": "The 'Fixed Interval' must be 0 (not used) or >= 3 (> 5 recommended)", + "display_text_as_no_gtsign": "The '>' between the ActualText and DisplayAsText is missing", + "display_text_as_no_actual": "The 'ActualText' is not specified", + "display_text_as_no_display_as": "The 'DisplayAsText' is not specified", + "not_found_directory": "The directory was not found", + "not_found_file": "The file was not found", + "duplicate_ic3_devicename": "This name is already used by another iCloud3 device", + "already_assigned": "This selection is already assigned to another device", + "mobapp_search_error": "WARNING: Search Failure - No Mobile App device that starts with the iCloud3 or Apple Acct devicename was found. Select 'None' or the device to be used.", + "duplicate_other_devicename": "This name is already used in another integration or platform", + "action_completed": "Requested Action has been completed", + "action_cancelled": "Requested Action has been cancelled", + "restart_ha": "Home Assistant needs to be restarted to continue", + "excluded_sensors_ha_restart": "Excluded Sensors were Updated. An HA restart is required" + + }, + "step": { + "menu": { + "title": "iCloud3 Configure Settings", + "data": { + "menu_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "menu_0": { + "title": "Configure Devices and Sensors Menu", + "data": { + "xmenu_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯", + "menu_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "menu_1": { + "title": "Configure Parameters Menu", + "data": { + "menu_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "restart_icloud3": { + "title": "Confirm Restarting iCloud3", + "description": "Note: Changes to tracked devices require restarting iCloud3", + "data": { + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "restart_ha_ic3": { + "title": "Restart Home Assistant or iCloud3", + "description": "Restart Home Assistant or reload and reinitialize the iCloud3 Integration\n\nTHE ICLOUD3 DASHBOARD SCREEN MUST BE REFRESHED AFTER RESTARTING", + "data": { + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "restart_ha_ic3_load_error": { + "title": "iCloud3 Load Error", + "description": "iCloud3 did not load and initialize when HA started. Reload iCloud3 again or restart HA", + "data": { + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + } + }, + "confirm_action": { + "title": "Confirm Selected Action", + "data": { + "confirm_action_form_hdr": "REQUESTED ACTION", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "data_source": { + "title": "Data Source - Apple Account, Mobile App", + "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", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + } + }, + "update_apple_acct": { + "title": "Update Apple Account Username/Password", + "data": { + "account_selected": "ACCOUNT SELECTED", + "username": "USERNAME - Email address/username used to sign in to the Apple Account", + "password": "PASSWORD - Account Password", + "totp_key": "TOTP KEY - Time Based One-Time Password Key used to generate the 6-digit authentication code", + "locate_all": "ALWAYS LOCATE ALL DEVICES - Locate all the devices in the Apple Account, including those in the Family list. Unchecked will only locate the Family list devices when they are being updated (default)", + "url_suffix_china": "CHINA USERS - Use Apple iCloud Web Servers located in China (.cn URL suffix)", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "locate_all": "Devices fall into two categories, your devices and everything else. Locating all devices will get their location with fewer internet calls that might take slightly longer while Apple locates them, especially if you have a lot of devices. Not doing this results in more internet calls that are faster." + } + }, + "delete_apple_acct": { + "title": "Remove an Apple Account", + "data": { + "account_selected": "ACCOUNT SELECTED", + "device_action": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ HOW SHOULD DEVICES ASSIGNED TO THIS APPLE ACCT BE HANDLED", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ CONFIRM DELETING THE APPLE ACCOUNT" + } + }, + "reauth": { + "title": "Apple ID Verification Code", + "description": "Enter the 6-digit verification code you just received from Apple", + "data": { + "account_selected": "APPLE ACCOUNT TO BE AUTHENTICATED", + "verification_code": "APPLE ACCOUNT VERIFICATION CODE", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "trusted_device": { + "title": "Apple Account Trusted Devices", + "description": "Select your Trusted Device", + "data": { + "trusted_device": "Trusted Device" + } + }, + "device_list": { + "title": "iCloud3 Devices", + "description": "Up to 10 devices can be tracked or monitored by iCloud3. They are listed here.\n\nThis screen is used to select a device that needs to be updated, add a new device and delete a device that should not be tracked any longer.", + "data": { + "devices": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "add_device": { + "title": "Add iCloud3 Device", + "data": { + "ic3_devicename": "ICLOUD3 DEVICE_TRACKER ENTITY ID - The HA device_tracker entity for this device. (Example: gary_iphone)", + "fname": "FRIENDLY NAME - Displayed in HA entities and on the Event Log (Example: Gary-iPhone)", + "device_type": "DEVICE TYPE - iPhone, iPad, Watch, etc.", + "tracking_mode": "TRACKING MODE - Location request method (Tracked, Monitored, Inactive)", + "mobapp": "MOBILE APP INSTALLED - HA Mobile App is installed on this device", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "ic3_devicename": "This is the device_tracker entity you are assigning to the iCloud3 device you want to track. The Apple Account and Mobile App devices to be associated with this device_tracker entity are selected on the next page. (Example: gary_iphone)" + } + }, + "update_device": { + "title": "Update iCloud3 Device", + "description": "This screen lets you configure each of the devices that can be tracked or monitored using iCloud3", + "data": { + "ic3_devicename": "ICLOUD3 DEVICE_TRACKER ENTITY ID - The HA device_tracker entity assigned to this device", + "fname": "FRIENDLY NAME - Displayed in HA device_tracker and sensor names and on the Event Log", + "device_type": "DEVICE TYPE - iPhone, iPad, Watch, etc.E", + "tracking_mode": "TRACKING MODE - How location requests should be done (Full tracking, Monitor, Inactive)", + "famshr_devicename": "APPLE ACCOUNT iCLOUD DEVICE - Apple iCloud device providing location data", + "mobile_app_device": "MOBILE APP DEVICE_TRACKER ENTITY - Mobile App device providing location data & zone triggers", + "picture": "PICTURE - Image of the person normally using this device (44x44 pixels is a good size)", + "inzone_interval": "INZONE INTERVAL", + "rarely_updated_parms": "RARELY UPDATED PARAMETERS - Select and Submit to update these items", + "log_zones": "ZONE LOG ACTIVITY - Enter/exit zone info (date, time, distance) is saved to a spreadsheet .csv file", + "track_from_zones": "TRACK-FROM-ZONES - Track travel time & distance from Home and other zones", + "track_from_base_zone": "TRACK-FROM-HOME ZONE OVERRIDE - Use this zone instead of the Home for tracking results", + "fixed_interval": "FIXED INTERVAL", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "inzone_interval": "Time between location requests when in a zone", + "fixed_interval": "A fixed time between location requests when not in a zone. iCloud3 calculates the interval for the next locate request and uses the calculated value if this is not set (= 0). This value will NOT be used when the calculated interval is less tnan 5-min, the current location data is old, the device is off-line or when the device is not in a zone.", + "track_from_base_zone": "Normally, the Home zone is used as the primary Track-from-Zone for all tracking (travel time, distance, etc). However, a different zone can be used as the primary Track-from-Zone if you are away from Home for an extended period or the device is normally at another location (vacation house, second home, parent's house, etc.). This can be set Globally for all devices on the Special Zones screen." + } + }, + "delete_device": { + "title": "Delete iCloud3 Device", + "data": { + "device_selected": "SELECTED DEVICE", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DELETE OPTIONS" + } + }, + "review_inactive_devices": { + "title": "Review Untracked (Inactive) Devices", + "description": "The 'Tracking Mode' of devices are set to 'Inactive' and will not be located or tracked.", + "data": { + "inactive_devices": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ INACTIVE ICLOUD3 DEVICES", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "change_device_order": { + "title": "Event Log Device Display Sequence", + "description": "The devices are displayed in the Event Log heading area and in various Event Log messages in the sequence below.\n\nThis screen lets you change the order of the devices.", + "data": { + "device_desc": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ICLOUD3 DEVICES", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "away_time_zone": { + "title": "Display Location Time Zone when Away", + "description": "The time displayed in the Event Log and Sensors show the time an event took place using the Home 'time zone' from your Home Assistant computer. When you are away from Home and in another time zone, your tracking events are still based on the time at your Home 'time zone', not time in your current location.\n\nThis screen lets you display time events using your current location's time zone.", + "data": { + "away_time_zone_1_devices": "Devices in Away Time Zone #1", + "away_time_zone_1_offset": "Time & Time Zone Adjustment at Current Location #1", + "away_time_zone_2_devices": "Devices in Away Time Zone #2", + "away_time_zone_2_offset": "Time & Time Zone Adjustment at Current Location #2", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "time_zone_1_offset": "PRIMARY DEVICES & LOCATION TIME - Devices and the current location time when away and in another time zone", + "time_zone_2_offset": "SECONDARY DEVICES & LOCATION TIME - Devices and the current location time when away and in another time zone" + } + }, + "format_settings": { + "title": "Format Settings", + "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": { + "log_level": "LOG LEVEL - The type of messages that are written to the iCloud3 Log file (icloud3-0.log}", + "log_level_devices": "RAWDATA LOG DEVICE FILTER - Write iCloud RawData to the log file for only these devices", + "display_zone_format": "EVENT LOG ZONE DISPLAY NAME - How the Zone name is displayed in sensors and the Event Log", + "device_tracker_state_source": "DEVICE TRACKER STATE VALUE - How the device's device_tracker entity state value is determined", + "time_format": "TIME FORMAT - How time fields are displayed in sensors and in the Event Log", + "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", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "log_level": "iCloud3 can log configuration parameters, startup activity and errors, tracking activity, error messages and the Apple Account requests for iCloud Device location RawData information (request and response). Log levels specify the type of records that should be written to the iCloud3 log file (`icloud3-0.log`) from basic (Info) to more detailed (Debug) to extremely detailed (RawData).", + "device_tracker_state_source": "HA uses the device's gps coordinates to determine the zone. The gps accuracy is not considered so the zone may be exited when the gps wanders out of the zone. iCloud3 does consider the gps accuracy and will not exit the zone when this occurs. iCloud3 will display the Zone's Friendly Name or zone name displayed on the Event Log." + } + }, + "display_text_as": { + "title": "Event Log 'Display Text As'", + "description": "There may be some text fields, such as email addresses or phone numbers, that are displayed on the Event Log screen that are private and should not be displayed. For example, you can replace 'geekstergary@apple.com' with 'gary@email.com'.\n\nThis screenis used to specify the original text and the text that should be displayed.", + "data": { + "display_text_as": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ TEXT REPLACEMENT FIELDS - [Actual text > Displayed text]", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "display_text_as_update": { + "title": "Update Event Log 'Display Text As' Value", + "data": { + "text_from": "ORIGINAL TEXT - Text to be replaced (example: gary_real_email@gmail.com)", + "text_to": "DISPLAYED TEXT- Text to be displayed (display: gary@email.com)", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + } + }, + "actions": { + "title": "iCloud3 Action Commands", + "data": { + "ic3_actions": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ICLOUD3 GENERAL CONTROL ACTIONS", + "debug_actions": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DEBUG LOG ACTIONS", + "other_actions": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ OTHER ACTIONS", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "inzone_intervals": { + "title": "inZone Parameters and Default Intervals", + "description": "An inZone interval is the time between location requests when the Device is in a zone.\n\n This screen is used to set the default values for different types of devices. This value is assigned to a device when it is added.\n\nNote: The inZone Interval can be set to a different value on the Update Device screen for each device.", + "data": { + "iphone": "IPHONE & IPOD", + "ipad": "IPAD", + "watch": "APPLE WATCH", + "airpods": "AIRPODS", + "no_mobapp": "MOBILE APP IS NOT INSTALLED", + "other": "OTHER DEVICE TYPE", + "distance_between_devices": "Determine the distance between devices. Use a near by device's tracking results", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "no_mobapp": "Default interval if the Mobile App is not used for location monitoring and zone enter/exit triggers", + "other": "Unspecified device type inzone interval" + } + }, + "waze_main": { + "title": "Waze - Route Service Travel Time/Distance", + "data": { + "waze_used": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ WAZE ROUTE SERVICE", + "waze_region": "ROUTE SERVER LOCATION - Location of the Waze Route Server for your area", + "waze_realtime": "USE REAL TIME DATA - Waze should consider traffic delays when determining travel time", + "waze_min_distance": "WAZE MINIMUM DISTANCE", + "waze_max_distance": "WAZE MAXIMUM DISTANCE", + "waze_history_database_used": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ WAZE HISTORY DATABASE", + "waze_history_track_direction": "GENERAL TRAVEL DIRECTION - Used to display 'Map Trace Lines' between saved locations", + "waze_history_max_distance": "HISTORY MAX DISTANCE", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "waze_min_distance": "Use the Waze Route Service when the zone distance is greater than this value", + "waze_max_distance": "Do not use the Waze Route Service when the zone distance is greater than than this value", + "waze_history_max_distance": "Do not save the Waze travel time & distance to the Waze History Database if the distance is greater than this value" + } + }, + "special_zones": { + "title": "Special Zones", + "description": "This screen is used to configure:\n  • Stationary Zones - Created when the device is in the same location for a short\n    period of time\n  • Enter Zone Delay Time - Delay processing a Zone Enter Trigger\n  • A temporary “home” zone at another location", + "data": { + "stat_zone_header": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ STATIONARY ZONE", + "stat_zone_fname": "FRIENDLY NAME BASE - Name to display when in a Stationary Zone (StatZone)", + "stat_zone_still_time": "NO MOVEMENT TIME", + "stat_zone_inzone_interval": "INZONE INTERVAL", + "passthru_zone_header": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ENTER ZONE DELAY", + "passthru_zone_time": "ENTER ZONE DELAY TIME", + "track_from_base_zone_used": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ PRIMARY TRACK-FROM-HOME ZONE OVERRIDE", + "track_from_base_zone": "TRACK FROM ZONE - Use this zone instead of Home for tracking results for all devices. Global setting", + "track_from_home_zone": "TRACK FROM HOME ZONE - Keep tracking from the Home zone when the Primary Track From Zone is not Home", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "passthru_zone_time": "Delay processing an Enter Zone Trigger that you may be driving through and not actually entering", + "stat_zone_still_time": "Time at the same location before moving into a Stationary Zone", + "stat_zone_inzone_interval": "Time interval between location requests when in a Stationary Zone", + "stat_zone_fname": "Display this value in the Event Log, device_tracker, travel direction and zone entities when the device is in a Stationary Zone. iCloud3 assigns a number to the Stationary Zone. Use the wildcard character '#' to display this value in the Stationary Zone's name (StatZon1, StatZon2, etc). Note: A 7-letter name leaves room for the name and number to be displayed on iPhone screens without being truncated." + } + }, + "sensors": { + "title": "Sensors", + "description": "Many sensors are used to display tracking results and other information for a device.\n\n This screen is used to select the sensors that should be created.", + "data": { + "monitored_devices": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ MONITORED DEVICE SENSORS - Select the type of sensors to create for a Monitored Device", + "device": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DEVICE SENSORS - Device status and information", + "tracking_update": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ LOCATION UPDATE SENSORS - Device location update times", + "tracking_time": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ TIME SENSORS - Device tracking timers", + "tracking_distance": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ DISTANCE SENSORS - Device tracking distances", + "track_from_zones": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ TRACK FROM MULTIPLE ZONE SENSORS - Used when tracking from more than one zone (not needed for tracking only from the Home zone)", + "tracking_other": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ OTHER TRACKING SENSORS - Not normally used but available", + "zone": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ZONE SENSORS - Device zone status and information", + "other": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ OTHER SENSORS - Sensors not in the above areas", + "excluded_sensors": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ EXCLUDED SENSORS - Sensors that will not be created when iCloud3 starts", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "exclude_sensors": { + "title": "Exclude Sensors", + "description": "Many sensors are created for the devices but there may be times when you want to not create a sensor for a specific device. For example, you may want to create a bettery sensor for all devices except one.\n\nThis screen lets you specify the sensor entity name that should not be created.", + "data": { + "excluded_sensors": "EXCLUDED SENSORS - Sensors that will not be created when iCloud3 starts", + "filter": "FILTER DISPLAYED SENSORS - Select the Sensors that should be displayed", + "filtered_sensors": "ICLOUD3 SENSORS - A list of Sensors that are created when iCloud3 starts", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + } + }, + "tracking_parameters": { + "title": "Tracking & Other Parameters", + "description": "Some parameters do not fall into any of the other general categories and are rarely changed.\n\nThis screen is used to specify those parameters.", + "data": { + "log_level": "LOG LEVEL - The type of messages that are added to the HA log file during iCloud3 operations", + "gps_accuracy_threshold": "GPS ACCURACY THRESHOLD", + "old_location_threshold": "OLD LOCATION THRESHOLD", + "old_location_adjustment": "OLD LOCATION ADJUSTMENT", + "distance_between_devices": "USE LOCATION RESULTS FROM A NEAR-BY DEVICE", + "max_interval": "MAXIMUM INTERVAL", + "exit_zone_interval": "EXIT ZONE INTERVAL", + "mobapp_alive_interval": "REQUEST MOBILE APP LOCATION INTERVAL", + "tfz_tracking_max_distance": "TRACK-FROM-ZONE DISPLAY DISTANCE", + "offline_interval": "DEVICE OFFLINE INTERVAL", + "travel_time_factor": "TRAVEL TIME INTERVAL AND NEXT LOCATION UPDATE MULTIPLIER", + "discard_poor_gps_inzone": "DISCARD POOR RESULTS - Discard Location Updates with Poor GPS Accuracy when in a Zone", + "picture_www_dirs": "WWW DIRECTORIES WITH PICTURE IMAGES - Filter for `Update Devices > Pictures` file locations", + "event_log_card_directory": "EVENT LOG CARD LOVELACE RESOURCES DIRECTORY - Event Log custom card .js file directory", + "event_log_btnconfig_url": "EVENT LOG CONFIGURE BUTTON (GEAR) URL > Special URL that display's the HA Configure Settings screen", + "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" + }, + "data_description": { + "old_location_threshold": "Locations older than this value will be discarded", + "old_location_adjustment": "Add this to the time that determines if a location is old", + "distance_between_devices": "When tracking results are updated, any nearby devices are identified. When tracking results for those devices are updated, the tracking results of the one originally updated can be used instead. This improves performance since the Waze route time and distance are not requested again", + "gps_accuracy_threshold": "Locations with GPS Accuracy above this value will be discarded", + "tfz_tracking_max_distance": "Normally the Home zone's time and distance data is displayed on the Device's device_tracker and sensor entities. Display the Track-from-Zone instead when the Device is within this distance of the Track-from-Zone", + "max_interval": "The maximum time between location requests", + "exit_zone_interval": "The time to the first location request after exiting a zone", + "mobapp_alive_interval": "Send a location request to the Mobile App if there has been no contact after this amount of time. This will check to see if the Mobile App is responding to location requests or is asleep and not running.", + "offline_interval": "Location request interval when offline (Airplane mode, dead cell area, etc.)", + "travel_time_factor": "This is used to calculate the Interval and Next Location Time when going towards Home. A smaller value will reduce the interval time and increase the location requests, a larger value will increase the interval and reduce the location requests.", + "event_log_btnconfig_url": "Normally, this is blank and iCloud3 will determine the URL for it's Configure Settings screen. However, if there is a problem caused by running HA in a virtual environment, docker or on another device and the actual URL can not be detemined, a 404 not found error may be encountered. If that happens, select it the normal way (HA Devices & Services > Integration > iCloud3 > Configure Settings gear) and copy the URL from the browser into this field." + } + } + } + }, + "services": { + "action": { + "name": "Action", + "description": "This service will send operational commands to iCloud3", + "fields": { + "command": { + "name": "Command", + "description": "(Required) The operational command to send to iCloud3" + }, + "device_name": { + "name": "Device Name", + "description": "(Optional) Apply all devices or only apply to the selected device" + } + } + }, + "update": { + "name": "Update", + "description": "The Update service has been replaced by the Action service" + }, + "restart": { + "name": "Restart", + "description": "This service will restart iCloud3" + }, + "find_iphone_alert": { + "name": "Find iPhone Alert Tone", + "description": "This service will send an alert tone to the device that you want to find", + "fields": { + "device_name": { + "name": "Device Name", + "description": "Device the Find iPhone Alert and Message should be sent to" + } + } + }, + "lost_device_alert": { + "name": "Send Lost Device Message", + "description": "This service will send a Message and Phone number to the lost iPhone", + "fields": { + "device_name": { + "name": "Device Name", + "description": "Device the Find iPhone Alert and Message should be sent to" + }, + "number": { + "name": "Phone Number", + "description": "The phone number to send the message to" + }, + "message": { + "name": "Message", + "description": "The message to be sent" + } + } + } + } +} diff --git a/custom_components/icloud3/strings.json b/custom_components/icloud3/strings.json index a0b639b..60d718c 100644 --- a/custom_components/icloud3/strings.json +++ b/custom_components/icloud3/strings.json @@ -69,6 +69,7 @@ "icloud_acct_logging_into": "Logging into Apple Account", "icloud_acct_logged_into": "✅ Logged into the Apple Account", "icloud_acct_already_logged_into": "Already Logged into the Apple Account", + "icloud_acct_locate_all_reqd": "All devices must be located. Other Family devices are tracked with this Apple Account", "icloud_acct_login_error_user_pw": "❌ Login Error, Invalid Username or Password", "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", @@ -97,7 +98,7 @@ "inactive_most_devices": "MOST Devices are INACTIVE and will not be located or tracked", "inactive_some_devices": "Some Devices are INACTIVE and will not be located or tracked", "inactive_few_devices": "A Few Devices are INACTIVE and will not be located or tracked", - "inactive_no_devices": "No Devices have been set up. Select `Add Device` and Submit", + "inactive_no_devices": "No Devices have been set up. On the `Update Devices` screen, select `Add a New Device`, then select `Submit` to display the screen to add an iCloud3 device_tracker entity.", "away_time_zone_dup_devices_1": "One of these devices is also selected in the Other Device List", "away_time_zone_dup_devices_2": "One of these devices is also selected in the Otner Device List", @@ -255,14 +256,15 @@ "add_device": { "title": "Add iCloud3 Device", "data": { - "ic3_devicename": "ICLOUD3 ENTITY ID - The HA device_tracker entity forto this device", - "fname": "FRIENDLY NAME - Displayed in HA entities and on the Event Log", + "ic3_devicename": "ICLOUD3 DEVICE_TRACKER ENTITY ID - The HA device_tracker entity for this device. (Example: gary_iphone)", + "fname": "FRIENDLY NAME - Displayed in HA entities and on the Event Log (Example: Gary-iPhone)", "device_type": "DEVICE TYPE - iPhone, iPad, Watch, etc.", "tracking_mode": "TRACKING MODE - Location request method (Tracked, Monitored, Inactive)", "mobapp": "MOBILE APP INSTALLED - HA Mobile App is installed on this device", "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" }, "data_description": { + "ic3_devicename": "This is the device_tracker entity you are assigning to the iCloud3 device you want to track. The Apple Account and Mobile App devices to be associated with this device_tracker entity are selected on the next page. (Example: gary_iphone)" } }, "update_device": { diff --git a/custom_components/icloud3/support/config_file.py b/custom_components/icloud3/support/config_file.py index dd5c09c..3d1cf16 100644 --- a/custom_components/icloud3/support/config_file.py +++ b/custom_components/icloud3/support/config_file.py @@ -36,7 +36,7 @@ CONF_PICTURE_WWW_DIRS, PICTURE_WWW_STANDARD_DIRS, RANGE_DEVICE_CONF, RANGE_GENERAL_CONF, MIN, MAX, STEP, RANGE_UM, CF_PROFILE, CF_DATA, CF_TRACKING, CF_GENERAL, CF_SENSORS, - CONF_DEVICES, CONF_APPLE_ACCOUNTS, + CONF_DEVICES, CONF_APPLE_ACCOUNTS, DEFAULT_APPLE_ACCOUNTS_CONF, ) from ..support import start_ic3 @@ -197,8 +197,13 @@ def _reconstruct_conf_file(): ''' Gb.conf_profile[CONF_UPDATE_DATE] = datetime_now() - Gb.conf_tracking[CONF_PASSWORD] = \ - encode_password(Gb.conf_tracking[CONF_PASSWORD]) + # Gb.conf_tracking[CONF_PASSWORD] = \ + # encode_password(Gb.conf_tracking[CONF_PASSWORD]) + + # for apple_acct in Gb.conf_apple_accounts: + # apple_acct[CONF_PASSWORD] = encode_password(apple_acct[CONF_PASSWORD]) + + encode_all_passwords() Gb.conf_tracking[CONF_APPLE_ACCOUNTS] = Gb.conf_apple_accounts Gb.conf_tracking[CONF_DEVICES] = Gb.conf_devices @@ -370,6 +375,11 @@ def conf_apple_acct(idx_or_username): - conf_apple_acct_idx = index ''' try: + if len(Gb.conf_apple_accounts) == 0: + conf_apple_acct = DEFAULT_APPLE_ACCOUNTS_CONF.copy() + Gb.conf_apple_accounts = [conf_apple_acct] + return (conf_apple_acct, 0) + if type(idx_or_username) is int: if isbetween(idx_or_username, 0, len(Gb.conf_apple_accounts)-1): conf_apple_acct = Gb.conf_apple_accounts[idx_or_username].copy() @@ -377,9 +387,10 @@ def conf_apple_acct(idx_or_username): return (conf_apple_acct, idx_or_username) elif type(idx_or_username) is str: - conf_apple_acct = [apple_account for apple_account in Gb.conf_apple_accounts - if apple_account[CONF_USERNAME] == idx_or_username] - conf_apple_acct_username = [apple_account[CONF_USERNAME] for apple_account in Gb.conf_apple_accounts] + conf_apple_acct = [apple_acct for apple_acct in Gb.conf_apple_accounts + if apple_acct[CONF_USERNAME] == idx_or_username] + conf_apple_acct_username = [apple_account[CONF_USERNAME] + for apple_account in Gb.conf_apple_accounts] conf_apple_acct_idx = conf_apple_acct_username.index(idx_or_username) if conf_apple_acct != []: @@ -419,9 +430,9 @@ def apple_acct_password_for_username(username): return '' try: - return [apple_account[CONF_PASSWORD] - for apple_account in Gb.conf_apple_accounts - if apple_account[CONF_USERNAME] == username][0] + return [apple_acct[CONF_PASSWORD] + for apple_acct in Gb.conf_apple_accounts + if apple_acct[CONF_USERNAME] == username][0] except: return '' @@ -890,11 +901,11 @@ def _insert_into_conf_dict_parameter(dict_parameter, # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> def encode_all_passwords(): + Gb.conf_tracking[CONF_PASSWORD] = encode_password(Gb.conf_tracking[CONF_PASSWORD]) for apple_acct in Gb.conf_apple_accounts: - apple_acct[CONF_PASSWORD] = \ - encode_password(apple_acct[CONF_PASSWORD]) + apple_acct[CONF_PASSWORD] = encode_password(apple_acct[CONF_PASSWORD]) #-------------------------------------------------------------------- def decode_all_passwords(): diff --git a/custom_components/icloud3/support/icloud_data_handler.py b/custom_components/icloud3/support/icloud_data_handler.py index d0c6ae6..7f6b6de 100644 --- a/custom_components/icloud3/support/icloud_data_handler.py +++ b/custom_components/icloud3/support/icloud_data_handler.py @@ -232,10 +232,11 @@ def update_PyiCloud_RawData_data(Device, results_msg_flag=True): device_id = None if Device.PyiCloud.locate_all_devices else Device.icloud_device_id locate_all_devices, device_id = _locate_all_or_acct_owner(Device) - if Device.PyiCloud.DeviceSvc: - Device.PyiCloud.DeviceSvc.refresh_client( requested_by_devicename=Device.devicename, - locate_all_devices=locate_all_devices, - device_id=device_id) + #if Device.PyiCloud.DeviceSvc: + #Device.PyiCloud.DeviceSvc.refresh_client( requested_by_devicename=Device.devicename, + Device.PyiCloud.refresh_icloud_data(requested_by_devicename=Device.devicename, + locate_all_devices=locate_all_devices, + device_id=device_id) if (Device.PyiCloud.response_code == 503 and Device.devicename not in Gb.username_pyicloud_503_connection_error): list_add(Gb.username_pyicloud_503_connection_error, Device.devicename) diff --git a/custom_components/icloud3/support/pyicloud_ic3.py b/custom_components/icloud3/support/pyicloud_ic3.py index 7617348..55205ec 100644 --- a/custom_components/icloud3/support/pyicloud_ic3.py +++ b/custom_components/icloud3/support/pyicloud_ic3.py @@ -89,6 +89,19 @@ 302: 'iCloud Server not Available (Connection Error)', } HTTP_RESPONSE_CODES_IDX = {str(code): code for code in HTTP_RESPONSE_CODES.keys()} + +DEVICE_DATA_FILTER_OUT = [ + 'features', 'scd', + 'rm2State', 'pendingRemoveUntilTS', 'repairReadyExpireTS', 'repairReady', 'lostModeCapable', 'wipedTimestamp', + 'encodedDeviceId', 'scdPh', 'locationCapable', 'trackingInfo', 'nwd', 'remoteWipe', 'canWipeAfterLock', 'baUUID', + 'snd', 'continueButtonTitle', 'alertText', 'cancelButtonTitle', 'createTimestamp', 'alertTitle', + 'lockedTimestamp', 'locFoundEnabled', 'lostDevice', 'pendingRemove', 'maxMsgChar', 'darkWake', 'wipeInProgress', + 'repairDeviceReason', 'deviceColor', 'deviceDiscoveryId', 'activationLocked', 'passcodeLength', + ] + # 'BTR', 'LLC', 'CLK', 'TEU', 'SND', 'ALS', 'CLT', 'PRM', 'SVP', 'SPN', 'XRM', 'NWF', 'CWP', + # 'MSG', 'LOC', 'LME', 'LMG', 'LYU', 'LKL', 'LST', 'LKM', 'WMG', 'SCA', 'PSS', 'EAL', 'LAE', 'PIN', + # 'LCK', 'REM', 'MCS', 'REP', 'KEY', 'KPD', 'WIP', + ''' https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitWebServicesReference/ErrorCodes.html#//apple_ref/doc/uid/TP40015240-CH4-SW1 @@ -116,6 +129,7 @@ LOCK_FAIL_PASSCODE_NOT_SET_CONS_FAIL = 2403 LOCK_FAIL_NO_PASSCD_2 = 2406 + app specific password notes "appIdKey=ba2ec180e6ca6e6c6a542255453b24d6e6e5b2be0cc48bc1b0d8ad64cfe0228f&appleId=APPLE_ID&password=password2&protocolVersion=A1234&userLocale=en_US&format=plist" --header "application/x-www-form-urlencoded" "https://idmsa.apple.com/IDMSWebAuth/clientDAW.cgi" @@ -568,81 +582,81 @@ def __init__( self, username, password=None, verify_login=False, config_flow_login=False): - if is_empty(username): - msg = "Apple Account username is not specified/558" - Gb.authenticate_method = 'Invalid username/password' - raise PyiCloudFailedLoginException(msg) - if is_empty(password): - msg = "Apple Account password is not specified/562" - Gb.authenticate_method = 'Invalid username/password' - raise PyiCloudFailedLoginException(msg) - - self.setup_time = time_now() - self.user = {"accountName": username, "password": password} - self.apple_id = username - self.username = username - self.username_base = username.split('@')[0] - self.username_base6 = self.username_base if Gb.log_debug_flag else f"{username[:6]}…" - - username_password = f"{username}:{password}" - upw = username_password.encode('ascii') - username_password_b64 = base64.b64encode(upw) - self.username_password_b64 = username_password_b64.decode('ascii') - - self.password = password - - self.locate_all_devices = False if locate_all_devices is False else True - self.is_authenticated = False # ICloud access has been authenticated via password or token - # self.requires_2sa = self._check_2sa_needed - self.requires_2fa = False # This is set during the authentication function - self.response_code_pwsrp_err = 0 - self.response_code = 0 - self.token_pw_data = {} - self.token_password = password - self.account_locked = False # set from the locked data item when authenticating with a token - self.account_name = '' - self.verify_login = verify_login - self.verification_code = None - self.authentication_alert_displayed_flag = False - self.update_requested_by = '' - self.endpoint_suffix = endpoint_suffix if endpoint_suffix else Gb.icloud_server_endpoint_suffix - self.config_flow_login = config_flow_login # Indicates this PyiCloud object is beinging created from config_flow - - self.cookie_directory = cookie_directory or Gb.icloud_cookie_directory - self.session_directory = session_directory or Gb.icloud_session_directory - self.cookie_filename = "".join([c for c in self.username if match(r"\w", c)]) - self.session_data = {} - self.session_data_token = {} - self.dsid = '' - self.trust_token = '' - self.session_token = '' - self.session_id = '' - self.connection_error_retry_cnt = 0 - - self.findme_url_root = None # iCloud url initialized from the accountLogin response data - self.HOME_ENDPOINT = "https://www.icloud.com" - self.SETUP_ENDPOINT = "https://setup.icloud.com/setup/ws/1" - self.AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth" - #self.AUTH_PASSWORD_ENDPOINT = "https://setup.icloud.com/setup/authenticate" - - if self.endpoint_suffix in APPLE_SPECIAL_ICLOUD_SERVER_COUNTRY_CODE: - self._setup_url_endpoint_suffix() - - self.PyiCloudSession = None - self.DeviceSvc = None # PyiCloud_ic3 object for Apple Device Service used to refresh the device's location - - self._initialize_variables() - self._setup_password_filter(password) - self._setup_PyiCloudSession() + try: + if is_empty(username): + msg = "Apple Account username is not specified/558" + Gb.authenticate_method = 'Invalid username/password' + raise PyiCloudFailedLoginException(msg) + if is_empty(password): + msg = "Apple Account password is not specified/562" + Gb.authenticate_method = 'Invalid username/password' + raise PyiCloudFailedLoginException(msg) + + self.setup_time = time_now() + self.user = {"accountName": username, "password": password} + self.apple_id = username + self.username = username + self.username_base = username.split('@')[0] + self.username_base6 = self.username_base if Gb.log_debug_flag else f"{username[:6]}…" + + username_password = f"{username}:{password}" + upw = username_password.encode('ascii') + username_password_b64 = base64.b64encode(upw) + self.username_password_b64 = username_password_b64.decode('ascii') + self.password = password + + self.locate_all_devices = locate_all_devices if locate_all_devices is not None else True + + self.is_authenticated = False # ICloud access has been authenticated via password or token + # self.requires_2sa = self._check_2sa_needed + self.requires_2fa = False # This is set during the authentication function + self.response_code_pwsrp_err = 0 + self.response_code = 0 + self.token_pw_data = {} + self.token_password = password + self.account_locked = False # set from the locked data item when authenticating with a token + self.account_name = '' + self.verify_login = verify_login + self.verification_code = None + self.authentication_alert_displayed_flag = False + self.update_requested_by = '' + self.endpoint_suffix = endpoint_suffix if endpoint_suffix else Gb.icloud_server_endpoint_suffix + self.config_flow_login = config_flow_login # Indicates this PyiCloud object is beinging created from config_flow + + self.cookie_directory = cookie_directory or Gb.icloud_cookie_directory + self.session_directory = session_directory or Gb.icloud_session_directory + self.cookie_filename = "".join([c for c in self.username if match(r"\w", c)]) + self.session_data = {} + self.session_data_token = {} + self.dsid = '' + self.trust_token = '' + self.session_token = '' + self.session_id = '' + self.connection_error_retry_cnt = 0 + + self.findme_url_root = None # iCloud url initialized from the accountLogin response data + self.HOME_ENDPOINT = "https://www.icloud.com" + self.SETUP_ENDPOINT = "https://setup.icloud.com/setup/ws/1" + self.AUTH_ENDPOINT = "https://idmsa.apple.com/appleauth/auth" + #self.AUTH_PASSWORD_ENDPOINT = "https://setup.icloud.com/setup/authenticate" + + if self.endpoint_suffix in APPLE_SPECIAL_ICLOUD_SERVER_COUNTRY_CODE: + self._setup_url_endpoint_suffix() + + self.PyiCloudSession = None + self.DeviceSvc = None # PyiCloud_ic3 object for Apple Device Service used to refresh the device's location + + self._initialize_variables() + self._setup_password_filter(password) + self._setup_PyiCloudSession() + + Gb.PyiCloudLoggingInto = self # Identifies a partial login that failed + Gb.PyiCloud_by_username[username] = self + self.authenticate() + self.refresh_icloud_data(locate_all_devices=True) - Gb.PyiCloudLoggingInto = self # Identifies a partial login that failed - Gb.PyiCloud_by_username[username] = self - self.authenticate() - # if self.DeviceSvc is None: - # self.create_DeviceSvc_object() - self.create_DeviceSvc_object() - if self.DeviceSvc: - self.DeviceSvc.refresh_client() + except Exception as err: + log_exception(err) return @@ -774,7 +788,7 @@ def authenticate(self, refresh_session=False): self.authenticate_method += ", Password" - if login_successful is False: + # The Auth with Token is necessary to fill in the findme_url if self._authenticate_with_token(): login_successful = True self.authenticate_method += ", Token" @@ -818,9 +832,9 @@ def authenticate(self, refresh_session=False): self._update_token_pw_file(CONF_PASSWORD, encode_password(self.token_password)) log_info_msg( f"{self.username_base}, " - f"Authentication Successful, {self.username_base}" + f"Authentication Successful, {self.username_base}, " f"Method-{self.authenticate_method}") - # self.authenticate_method = f"{self.account_owner_short}, {self.authenticate_method}" + self.is_authenticated = self.is_authenticated or login_successful #---------------------------------------------------------------------------- @@ -1523,7 +1537,7 @@ def create_DeviceSvc_object(self, config_flow_login=False): ''' try: if self.DeviceSvc: - return + return self.DeviceSvc self.DeviceSvc = PyiCloud_DeviceSvc(self, self.PyiCloudSession, @@ -1539,13 +1553,29 @@ def create_DeviceSvc_object(self, config_flow_login=False): return None #---------------------------------------------------------------------------- - @property - def refresh_icloud_data(self): + def refresh_icloud_data(self, locate_all_devices=None, + requested_by_devicename=None, + device_id=None): ''' Refresh the iCloud device data for all devices and update the PyiCloud_RawData object for all locatible devices that are being tracked by iCloud3. ''' - self.DeviceSvc.refresh_client() + try: + if self.DeviceSvc is None: + self.create_DeviceSvc_object() + + locate_all_devices = locate_all_devices if locate_all_devices is not None \ + else self.locate_all_devices if self.locate_all_devices is not None \ + else True + + self.DeviceSvc.refresh_client( locate_all_devices=locate_all_devices, + requested_by_devicename=requested_by_devicename, + device_id=device_id) + + return + + except Exception as err: + log_exception(err) #---------------------------------------------------------------------------- def play_sound(self, device_id, subject="Find My iPhone Alert"): @@ -1629,18 +1659,18 @@ def __init__(self, PyiCloud, # This will generate an error if the table has not been defined from (init or start_ic3) # Init may be in the process of setting up the table and iCloud but then start_ic3/Stage 4 # thinks it is not done and resets everything. - if self.PyiCloud.device_id_by_icloud_dname != {}: + if isnot_empty(self.PyiCloud.device_id_by_icloud_dname): return except: pass - self.refresh_client(locate_all_devices=True) + self.refresh_client() Gb.devices_not_set_up = self._get_conf_icloud_devices_not_set_up() if Gb.devices_not_set_up == []: return - self.refresh_client(locate_all_devices=True) + #self.refresh_client() #---------------------------------------------------------------------------- def _get_conf_icloud_devices_not_set_up(self): @@ -1689,56 +1719,62 @@ def refresh_client(self, requested_by_devicename=None, = True - Locate all devices in the Family Sharing list (overrides device selection) = False - Locate only the devices belonging to this Apple acct ''' - if self.is_DeviceSvc_setup_complete is False: - return - - locate_all_devices = True if locate_all_devices is None else locate_all_devices - - if requested_by_devicename: - _Device = Gb.Devices_by_devicename[requested_by_devicename] - last_update_loc_time = _Device.last_update_loc_time - else: - last_update_loc_time = '?' + try: + if self.is_DeviceSvc_setup_complete is False: + return False - if locate_all_devices is False: - device_msg = f"OwnerDev-{len(Gb.owner_device_ids_by_username[self.PyiCloud.username])}" - else: - device_msg = f"AllDevices-{len(Gb.Devices_by_username.get(self.PyiCloud.username, []))}" + #locate_all_devices = True if locate_all_devices is None else locate_all_devices + locate_all_devices = locate_all_devices if locate_all_devices is not None \ + else self.PyiCloud.locate_all_devices if self.PyiCloud.locate_all_devices is not None \ + else True - log_debug_msg( f"Apple Acct > {self.PyiCloud.username_base}, " - f"RefreshRequestBy-{requested_by_devicename}, " - f"LocateAllDev-{locate_all_devices}, {device_msg}, LastLoc-{last_update_loc_time}") + if requested_by_devicename: + _Device = Gb.Devices_by_devicename[requested_by_devicename] + last_update_loc_time = _Device.last_update_loc_time + else: + last_update_loc_time = '?' - url = f"{self.PyiCloud.findme_url_root}{REFRESH_ENDPOINT}" - data = {"clientContext":{ - "fmly": locate_all_devices, - "shouldLocate": True, - "selectedDevice": device_id, + if locate_all_devices is False: + device_msg = f"OwnerDev-{len(Gb.owner_device_ids_by_username.get(self.PyiCloud.username, []))}" + else: + device_msg = f"AllDevices-{len(Gb.Devices_by_username.get(self.PyiCloud.username, []))}" + + log_debug_msg( f"Apple Acct > {self.PyiCloud.username_base}, " + f"RefreshRequestBy-{requested_by_devicename}, " + f"LocateAllDev-{locate_all_devices}, {device_msg}, LastLoc-{last_update_loc_time}") + + url = f"{self.PyiCloud.findme_url_root}{REFRESH_ENDPOINT}" + data = {"clientContext":{ + "fmly": locate_all_devices, + "shouldLocate": True, + "selectedDevice": device_id, + "deviceListVersion": 1, }, + "accountCountryCode": self.PyiCloud.session_data_token.get("account_country"), + "dsWebAuthToken": self.PyiCloud.session_data_token.get("session_token"), + "trustToken": self.PyiCloud.session_data_token.get("trust_token", ""), + "extended_login": True,} - "deviceListVersion": 1, }, - "accountCountryCode": self.PyiCloud.session_data_token.get("account_country"), - "dsWebAuthToken": self.PyiCloud.session_data_token.get("session_token"), - "trustToken": self.PyiCloud.session_data_token.get("trust_token", ""), - "extended_login": True,} + try: + devices_data = self.PyiCloudSession.post(url, params=self.params, data=data) + self.devices_data = devices_data.json() + + except Exception as err: + self.devices_data = {} + log_debug_msg(f"{self.PyiCloud.username_base}, No data returned from iCloud refresh request") + + if self.PyiCloudSession.response_code == 501: + self._set_service_available(False) + post_event( f"{EVLOG_ALERT}iCLOUD ALERT > {self.PyiCloud.account_owner}, " + f"Family Sharing Data Source is not available. " + f"The web url providing location data returned a " + f"Service Not Available error") + return None - try: - devices_data = self.PyiCloudSession.post(url, params=self.params, data=data) - self.devices_data = devices_data.json() + self.update_device_location_data(requested_by_devicename, self.devices_data.get("content", {})) + return isnot_empty(self.PyiCloud.RawData_by_device_id) except Exception as err: - self.devices_data = {} - log_debug_msg(f"{self.PyiCloud.username_base}, No data returned from iCloud refresh request") - - if self.PyiCloudSession.response_code == 501: - self._set_service_available(False) - post_event( f"{EVLOG_ALERT}iCLOUD ALERT > {self.PyiCloud.account_owner}, " - f"Family Sharing Data Source is not available. " - f"The web url providing location data returned a " - f"Service Not Available error") - return None - - self.PyiCloud.last_refresh_secs = time_now_secs() - self.update_device_location_data(requested_by_devicename, self.devices_data.get("content", {})) + log_exception(err) #---------------------------------------------------------------------------- def update_device_location_data(self, requested_by_devicename=None, devices_data=None): @@ -1799,20 +1835,21 @@ def update_device_location_data(self, requested_by_devicename=None, devices_data # else: device_msg = self._create_iCloud_RawData_object(device_id, device_data_name, device_data) monitor_msg += device_msg - continue + #continue # The PyiCloudSession is not recreated on a restart if it already is valid but we need to # initialize all devices, not just tracked ones on an iC3 restart. elif Gb.start_icloud3_inprocess_flag: device_msg = self._initialize_iCloud_RawData_object(device_id, device_data_name, device_data) monitor_msg += device_msg - continue + #continue # Non-tracked devices are not updated _RawData = self.PyiCloud.RawData_by_device_id[device_id] _Device = _RawData.Device - if _RawData.Device is None: + if (_RawData.Device is None + or 'location' not in _RawData.device_data): continue _RawData.save_new_device_data(device_data) @@ -1938,7 +1975,7 @@ def _initialize_iCloud_RawData_object(self, device_id, device_data_name, device_ log_debug_msg( f"Initialize RawData_icloud object " f"{self.PyiCloud.username_base}{LINK}<{_RawData.fname}, " f"{device_data_name}") - + # if ('all' in Gb.conf_general[CONF_LOG_LEVEL_DEVICES] # or _RawData.ic3_devicename in Gb.conf_general[CONF_LOG_LEVEL_DEVICES]): # Log all devices (no filter) on initialization @@ -2116,6 +2153,7 @@ def __init__(self, device_id, self.Device = Gb.Devices_by_devicename.get(self.ic3_devicename) self.device_data = device_data + self.status_code = 0 self.update_secs = time_now_secs() self.location_secs = 0 @@ -2317,8 +2355,16 @@ def save_new_device_data(self, device_data): except: self.last_loc_time_gps = '' - self.device_data.clear() - self.device_data.update(device_data) + try: + self.status_code = device_data['snd']['status_code'] + except: + pass + + filtered_device_data = {k: v for k, v in device_data.items() + if k not in DEVICE_DATA_FILTER_OUT} + + # self.device_data.clear() + self.device_data.update(filtered_device_data) self.set_located_time_battery_info() self.device_data[DATA_SOURCE] = self.data_source @@ -2330,7 +2376,7 @@ def status(self, additional_fields=[]): Returns status information for device. This returns only a subset of possible properties. ''' - self.DeviceSvc.refresh_client(self.device_id) + self.DeviceSvc.refresh_client(requested_by_devicename=self.device_id) fields = ["batteryLevel", "deviceDisplayName", "deviceStatus", "name"] fields += additional_fields diff --git a/custom_components/icloud3/support/pyicloud_ic3_interface.py b/custom_components/icloud3/support/pyicloud_ic3_interface.py index 5fcabf7..205ce97 100644 --- a/custom_components/icloud3/support/pyicloud_ic3_interface.py +++ b/custom_components/icloud3/support/pyicloud_ic3_interface.py @@ -45,9 +45,10 @@ def create_all_PyiCloudServices(): post_event('Log into Apple Accounts') for conf_apple_acct in Gb.conf_apple_accounts: + username = conf_apple_acct[CONF_USERNAME] password = Gb.PyiCloud_password_by_username[username] - locate_all = conf_apple_acct[CONF_LOCATE_ALL] + locate_all_devices = conf_apple_acct[CONF_LOCATE_ALL] if is_empty(username) or is_empty(password): continue @@ -62,7 +63,7 @@ def create_all_PyiCloudServices(): Gb.username_valid_by_username[username] = username_password_valid if Gb.username_valid_by_username[username]: - log_into_apple_account(username, password, locate_all) + log_into_apple_account(username, password, locate_all_devices) else: event_msg =(f"Apple Acct > " f"{username.split('@')[0]}, Invalid Username or Password") @@ -84,9 +85,9 @@ 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] - locate_all = conf_apple_acct[CONF_LOCATE_ALL] + locate_all_devices = conf_apple_acct[CONF_LOCATE_ALL] - PyiCloud = log_into_apple_account(username, password, locate_all) + PyiCloud = log_into_apple_account(username, password, locate_all_devices) if PyiCloud: post_event(f"{EVLOG_ERROR}Apple Acct > {PyiCloud.account_owner}, Login Successful") @@ -112,8 +113,9 @@ def verify_all_apple_accounts(): cnt = -1 for conf_apple_acct in Gb.conf_apple_accounts: cnt += 1 - username = conf_apple_acct[CONF_USERNAME] - password = Gb.PyiCloud_password_by_username[username] + + username = conf_apple_acct[CONF_USERNAME] + password = Gb.PyiCloud_password_by_username[username] if is_empty(username): Gb.username_valid_by_username[f"AA-NOTSPECIFIED-#{cnt}"] @@ -133,7 +135,7 @@ def verify_all_apple_accounts(): #-------------------------------------------------------------------- -def log_into_apple_account(username, password, locate_all=True): +def log_into_apple_account(username, password, locate_all_devices=None): ''' Log in and Authenticate the Apple Account via pyicloud @@ -147,7 +149,11 @@ def log_into_apple_account(username, password, locate_all=True): or password == ''): return - this_fct_error_flag = True + locate_all_devices = locate_all_devices \ + if locate_all_devices is not None \ + else PyiCloud.locate_all_devices if PyiCloud.locate_all_devices is not None \ + else True + login_err = 0 post_evlog_greenbar_msg(f"Apple Acct > Setting up {username.split('@')[0]}") @@ -172,7 +178,8 @@ def log_into_apple_account(username, password, locate_all=True): and PyiCloud.DeviceSvc): PyiCloud.dup_icloud_dname_cnt = {} - PyiCloud.DeviceSvc.refresh_client() + # PyiCloud.DeviceSvc.refresh_client() + PyiCloud.refresh_icloud_data(locate_all_devices=True) pyicloud_msg = f"{PyiCloud=} {PyiCloud.is_DeviceSvc_setup_complete=} {PyiCloud.DeviceSvc=}" if PyiCloud.DeviceSvc: @@ -184,36 +191,58 @@ def log_into_apple_account(username, password, locate_all=True): elif (PyiCloud and PyiCloud.is_DeviceSvc_setup_complete): - PyiCloud.create_DeviceSvc_object() - Gb.PyiCloud_by_username[username] = PyiCloud + try: + PyiCloud.create_DeviceSvc_object() + Gb.PyiCloud_by_username[username] = PyiCloud - pyicloud_msg = f"{PyiCloud=} {PyiCloud.is_DeviceSvc_setup_complete=} {PyiCloud.DeviceSvc=}" - if PyiCloud.DeviceSvc: - pyicloud_msg += f"{PyiCloud.RawData_by_device_id.values()=}" - log_debug_msg(f"{debug_msg_hdr}2, Create DeviceSvc, {pyicloud_msg}") - post_event(f"Apple Acct > {PyiCloud.account_owner}, iCloud Created & Refreshed") + pyicloud_msg = f"{PyiCloud=} {PyiCloud.is_DeviceSvc_setup_complete=} {PyiCloud.DeviceSvc=}" + if PyiCloud.DeviceSvc: + pyicloud_msg += f"{PyiCloud.RawData_by_device_id.values()=}" + log_debug_msg(f"{debug_msg_hdr}2, Create DeviceSvc, {pyicloud_msg}") + post_event(f"Apple Acct > {PyiCloud.account_owner}, iCloud Created & Refreshed") - return PyiCloud + return PyiCloud + + except Exception as err: + log_exception(err) # Setup PyiCloud and iCloud else: - PyiCloud = PyiCloudService( username, password, - locate_all_devices=locate_all, - cookie_directory=Gb.icloud_cookie_directory, - session_directory=Gb.icloud_session_directory) - - # Stage 4 checks to see if PyiCloud exists and it has RawData device info. These values exists - # if the __init__ login was completed. However, if it was not completed and they do not exist, - # Stage 4 will do another login and set these values when it finishes, which is before the - # __init__ is complete. Do not set them again when __init__ login finially completes. - - pyicloud_msg = (f"{PyiCloud.account_owner_username}, " - f"Complete={PyiCloud.is_DeviceSvc_setup_complete}, ") + try: + + PyiCloud = None + PyiCloud = PyiCloudService( username, password, + locate_all_devices=locate_all_devices, + cookie_directory=Gb.icloud_cookie_directory, + session_directory=Gb.icloud_session_directory) + + # Stage 4 checks to see if PyiCloud exists and it has RawData device info. These values exists + # if the __init__ login was completed. However, if it was not completed and they do not exist, + # Stage 4 will do another login and set these values when it finishes, which is before the + # __init__ is complete. Do not set them again when __init__ login finially completes. + + pyicloud_msg = (f"{PyiCloud.account_owner_username}, " + f"Complete={PyiCloud.is_DeviceSvc_setup_complete}, ") + + except Exception as err: + log_exception(err) + if PyiCloud.DeviceSvc: - rawdata_items = [_RawData.fname_device_id for _RawData in PyiCloud.RawData_by_device_id.values()] + if is_empty(PyiCloud.RawData_by_device_id): + PyiCloud.refresh_icloud_data(locate_all_devices=True) + + rawdata_items = [_RawData.fname_device_id + for _RawData in PyiCloud.RawData_by_device_id.values()] + + if is_empty(rawdata_items): + PyiCloud.refresh_icloud_data(locate_all_devices=True) + + rawdata_items = [_RawData.fname_device_id + for _RawData in PyiCloud.RawData_by_device_id.values()] + pyicloud_msg += f"RawDataItems-({list_to_str(rawdata_items)})" - log_debug_msg(f"{debug_msg_hdr}3, Setup PyiCloud, {pyicloud_msg}") + log_debug_msg(f"{debug_msg_hdr}3, Setup PyiCloud, {pyicloud_msg}") post_event(f"Apple Acct > {PyiCloud.account_owner}, Login Successful") verify_icloud_device_info_received(PyiCloud) @@ -274,7 +303,7 @@ def verify_icloud_device_info_received(PyiCloud): f"Family Sharing List Refresh " f"(#{Gb.get_ICLOUD_devices_retry_cnt} of 8)") - PyiCloud.DeviceSvc.refresh_client(locate_all_devices=True) + PyiCloud.refresh_icloud_data(locate_all_devices=True) if PyiCloud.DeviceSvc.devices_cnt >= 0: return True diff --git a/custom_components/icloud3/support/start_ic3.py b/custom_components/icloud3/support/start_ic3.py index 91c257d..5ca2d70 100644 --- a/custom_components/icloud3/support/start_ic3.py +++ b/custom_components/icloud3/support/start_ic3.py @@ -9,7 +9,7 @@ EVLOG_ALERT, EVLOG_IC3_STARTING, EVLOG_NOTICE, EVLOG_IC3_STAGE_HDR, CIRCLE_LETTERS_DARK, EVENT_RECDS_MAX_CNT_BASE, EVENT_RECDS_MAX_CNT_ZONE, - CRLF, CRLF_DOT, CRLF_CHK, CRLF_SP3_DOT, HDOT, CRLF_SP5_DOT, CRLF_HDOT, LINK, LLINK, RLINK, + CRLF, CRLF_DOT, CRLF_CHK, CRLF_SP3_DOT, HDOT, CRLF_SP5_DOT, CRLF_HDOT, LINK, LLINK, RLINK, CRLF_CIRCLE_X, CRLF_SP3_HDOT, CRLF_INDENT, CRLF_X, CRLF_TAB, DOT, CRLF_SP8_HDOT, CRLF_SP8_DOT, CRLF_RED_X, RED_X, CRLF_STAR, CRLF_YELLOW_ALERT, YELLOW_ALERT, UNKNOWN, RARROW, NBSP2, NBSP4, NBSP6, CIRCLE_STAR, INFO_SEPARATOR, DASH_20, CHECK_MARK, @@ -1147,7 +1147,7 @@ def create_Devices_object(): post_startup_alert(f"HA device_tracker entity id not configured for {icloud_dname}") post_event( f"{EVLOG_ALERT}CONFIGURATION ALERT > The device_tracker entity id (devicename) " f"has not been configured for {icloud_dname}/" - f"{DEVICE_TYPE_FNAME.get(conf_device[CONF_DEVICE_TYPE], conf_device[CONF_DEVICE_TYPE])}") + f"{DEVICE_TYPE_FNAME(conf_device[CONF_DEVICE_TYPE])}") continue Gb.conf_icloud_dnames.append(icloud_dname) @@ -1155,7 +1155,7 @@ def create_Devices_object(): if conf_device[CONF_TRACKING_MODE] == INACTIVE_DEVICE: post_event( f"{CIRCLE_STAR} {conf_device[CONF_FNAME]} ({devicename}) > " - f"{DEVICE_TYPE_FNAME.get(conf_device[CONF_DEVICE_TYPE], conf_device[CONF_DEVICE_TYPE])}, INACTIVE, " + f"{DEVICE_TYPE_FNAME(conf_device[CONF_DEVICE_TYPE])}, INACTIVE, " f"{CRLF_DOT}iCloud Device-{icloud_dname}" f"{CRLF_DOT}MobApp Entity-{conf_device[CONF_MOBILE_APP_DEVICE]}") continue @@ -1238,7 +1238,8 @@ def create_Devices_object(): _verify_away_time_zone_devicenames() Gb.startup_lists['Gb.Devices'] = Gb.Devices - Gb.startup_lists['Gb.DevDevices_by_devicename'] = Gb.Devices_by_devicename + Gb.startup_lists['Gb.DeviceTrackers_by_devicename']= Gb.DeviceTrackers_by_devicename + Gb.startup_lists['Gb.Devices_by_devicename'] = Gb.Devices_by_devicename Gb.startup_lists['Gb.conf_devicenames'] = Gb.conf_devicenames Gb.startup_lists['Gb.conf_icloud_dnames'] = Gb.conf_icloud_dnames Gb.startup_lists['Gb.devicenames_by_icloud_dname'] = Gb.devicenames_by_icloud_dname @@ -1297,6 +1298,10 @@ def setup_data_source_ICLOUD(retry=False): apple_acct_not_found_msg = '' for username, PyiCloud in Gb.PyiCloud_by_username.items(): + if is_empty(PyiCloud.RawData_by_device_id): + #PyiCloud.DeviceSvc.refresh_client(locate_all_devices=True) + PyiCloud.refresh_icloud_data() + if PyiCloud and Gb.username_valid_by_username.get(username): setup_tracked_devices_for_icloud(PyiCloud) set_device_data_source(PyiCloud) @@ -1459,7 +1464,7 @@ def _match_PyiCloud_devices_to_Device(PyiCloud, retry_match_devices=None): icloud_dname = conf_device[CONF_FAMSHR_DEVICENAME] Gb.devicenames_by_icloud_dname[icloud_dname] = devicename - Gb.icloud_dnames_by_devicename[devicename] = icloud_dname + Gb.icloud_dnames_by_devicename[devicename] = icloud_dname _RawData.Device = Device _RawData.ic3_devicename = devicename @@ -1513,7 +1518,8 @@ def _check_for_missing_find_devices(PyiCloud): retry_cnt = 0 if PyiCloud.DeviceSvc else 10 while retry_cnt < 5: retry_cnt += 1 - PyiCloud.DeviceSvc.refresh_client() + #PyiCloud.DeviceSvc.refresh_client() + PyiCloud.refresh_icloud_data() # See in the untracked devices are now available retry_match_devices = {} @@ -1691,10 +1697,13 @@ def _post_evlog_apple_acct_tracked_devices_info(PyiCloud): f"{devices_assigned_msg}" f"{devices_not_assigned_msg}") + famshr_crlf = CRLF_DOT if PyiCloud.locate_all_devices else CRLF_CIRCLE_X if owner_icloud_dnames: evlog_msg += f"{CRLF_DOT} myDevices-{owner_icloud_dnames}" if famshr_dnames_msg: - evlog_msg += f"{CRLF_DOT} FamShr List Devices-{famshr_dnames_msg}" + evlog_msg += f"{famshr_crlf} Family List-{famshr_dnames_msg}" + if PyiCloud.locate_all_devices is False: + evlog_msg += f"{CRLF_STAR} Family List Devices are Not Located" post_event(evlog_msg) @@ -2454,8 +2463,7 @@ def display_inactive_devices(): inactive_devices =[(f"{conf_device[CONF_IC3_DEVICENAME]} (" f"{conf_device[CONF_FNAME]}/" - f"{DEVICE_TYPE_FNAME.get( - conf_device[CONF_DEVICE_TYPE], conf_device[CONF_DEVICE_TYPE])})") + f"{DEVICE_TYPE_FNAME(conf_device[CONF_DEVICE_TYPE])})") for conf_device in Gb.conf_devices if conf_device[CONF_TRACKING_MODE] == INACTIVE_DEVICE] diff --git a/custom_components/icloud3/support/start_ic3_control.py b/custom_components/icloud3/support/start_ic3_control.py index 95e1f2d..d005d83 100644 --- a/custom_components/icloud3/support/start_ic3_control.py +++ b/custom_components/icloud3/support/start_ic3_control.py @@ -20,7 +20,7 @@ from ..helpers.messaging import (broadcast_info_msg, post_event, post_error_msg, log_error_msg, post_startup_alert, post_monitor_msg, post_internal_error, - log_start_finish_update_banner, + write_ic3log_recd, log_debug_msg, log_warning_msg, log_info_msg, log_exception, log_rawdata, _evlog, _log, more_info, format_filename, write_config_file_to_ic3log, @@ -37,6 +37,12 @@ def stage_1_setup_variables(): stage_title = f'Stage 1 > Initial Preparations' open_ic3log_file() + + # if Gb.prestartup_log: + # _prestartup_log = Gb.prestartup_log + # Gb.prestartup_log = '' + # write_ic3log_recd(f"$$$$$\n#####\n{_prestartup_log}") + log_info_msg(f"* > {EVLOG_IC3_STAGE_HDR}{stage_title}") broadcast_info_msg(stage_title) @@ -346,13 +352,13 @@ def _log_into_apple_accounts(retry=False): PyiCloud = pyicloud_ic3_interface.log_into_apple_account( username, Gb.PyiCloud_password_by_username[username], - locate_all=conf_apple_acct[CONF_LOCATE_ALL]) + locate_all_devices=conf_apple_acct[CONF_LOCATE_ALL]) if PyiCloud: Gb.PyiCloud_by_username[username] = PyiCloud if is_empty(Gb.devices_without_location_data): - post_event("Apple Acct > All Devices Located") + post_event(f"Apple Acct > {PyiCloud.username_base}, All Devices Located") else: post_event(f"Apple Acct > Devices not Located > {list_to_str(Gb.devices_without_location_data)}") return False @@ -380,8 +386,8 @@ def _are_all_devices_verified(retry=False): for devicename, Device in Gb.Devices_by_devicename.items() if Device.verified_flag is False and Device.isnot_inactive] - Gb.usernames_setup_error_retry_list = unverified_device_usernames - Gb.devicenames_setup_error_retry_list = unverified_devices + Gb.usernames_setup_error_retry_list = list(set(unverified_device_usernames)) + Gb.devicenames_setup_error_retry_list = list(set(unverified_devices)) Gb.startup_lists['_.usernames_setup_error_retry_list'] = Gb.usernames_setup_error_retry_list Gb.startup_lists['_.devicenames_setup_error_retry_list'] = Gb.devicenames_setup_error_retry_list diff --git a/custom_components/icloud3/support/zone_handler.py b/custom_components/icloud3/support/zone_handler.py index de0ed45..b8be73a 100644 --- a/custom_components/icloud3/support/zone_handler.py +++ b/custom_components/icloud3/support/zone_handler.py @@ -311,7 +311,9 @@ def is_same_or_overlapping_zone(zone1, zone2): if zone1 == zone2: return True - if (isnot_zone(zone1) or zone1 == 'not_set' or zone2 == 'not_set' + if (isnot_zone(zone1) + or zone1 not in Gb.Zones_by_zone or zone2 not in Gb.Zones_by_zone + or zone1 == 'not_set' or zone2 == 'not_set' or zone1 == "" or zone2 == ""): return False @@ -323,7 +325,7 @@ def is_same_or_overlapping_zone(zone1, zone2): return (zone_dist_m <= 2) except Exception as err: - log_exception(err) + #log_exception(err) return False #-------------------------------------------------------------------- diff --git a/custom_components/icloud3/translations/en.json b/custom_components/icloud3/translations/en.json index a0b639b..60d718c 100644 --- a/custom_components/icloud3/translations/en.json +++ b/custom_components/icloud3/translations/en.json @@ -69,6 +69,7 @@ "icloud_acct_logging_into": "Logging into Apple Account", "icloud_acct_logged_into": "✅ Logged into the Apple Account", "icloud_acct_already_logged_into": "Already Logged into the Apple Account", + "icloud_acct_locate_all_reqd": "All devices must be located. Other Family devices are tracked with this Apple Account", "icloud_acct_login_error_user_pw": "❌ Login Error, Invalid Username or Password", "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", @@ -97,7 +98,7 @@ "inactive_most_devices": "MOST Devices are INACTIVE and will not be located or tracked", "inactive_some_devices": "Some Devices are INACTIVE and will not be located or tracked", "inactive_few_devices": "A Few Devices are INACTIVE and will not be located or tracked", - "inactive_no_devices": "No Devices have been set up. Select `Add Device` and Submit", + "inactive_no_devices": "No Devices have been set up. On the `Update Devices` screen, select `Add a New Device`, then select `Submit` to display the screen to add an iCloud3 device_tracker entity.", "away_time_zone_dup_devices_1": "One of these devices is also selected in the Other Device List", "away_time_zone_dup_devices_2": "One of these devices is also selected in the Otner Device List", @@ -255,14 +256,15 @@ "add_device": { "title": "Add iCloud3 Device", "data": { - "ic3_devicename": "ICLOUD3 ENTITY ID - The HA device_tracker entity forto this device", - "fname": "FRIENDLY NAME - Displayed in HA entities and on the Event Log", + "ic3_devicename": "ICLOUD3 DEVICE_TRACKER ENTITY ID - The HA device_tracker entity for this device. (Example: gary_iphone)", + "fname": "FRIENDLY NAME - Displayed in HA entities and on the Event Log (Example: Gary-iPhone)", "device_type": "DEVICE TYPE - iPhone, iPad, Watch, etc.", "tracking_mode": "TRACKING MODE - Location request method (Tracked, Monitored, Inactive)", "mobapp": "MOBILE APP INSTALLED - HA Mobile App is installed on this device", "action_items": "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ ACTION COMMANDS" }, "data_description": { + "ic3_devicename": "This is the device_tracker entity you are assigning to the iCloud3 device you want to track. The Apple Account and Mobile App devices to be associated with this device_tracker entity are selected on the next page. (Example: gary_iphone)" } }, "update_device": {