From 7a8a59f06df626de4ab940dfe0cf5a415660f332 Mon Sep 17 00:00:00 2001 From: gcobb321 Date: Tue, 10 Oct 2023 16:04:32 -0400 Subject: [PATCH] iCloud3 v3.0.rc6 --- custom_components/icloud3/ChangeLog.txt | 10 +- custom_components/icloud3/__init__.py | 18 +- custom_components/icloud3/config_flow.py | 208 +++++++++--------- custom_components/icloud3/const.py | 6 +- custom_components/icloud3/device.py | 7 +- custom_components/icloud3/global_variables.py | 1 + custom_components/icloud3/icloud3_main.py | 40 ++-- custom_components/icloud3/sensor.py | 7 +- .../icloud3/support/determine_interval.py | 41 ++-- .../icloud3/support/pyicloud_ic3.py | 9 +- .../icloud3/support/service_handler.py | 13 +- .../icloud3/support/start_ic3.py | 3 +- .../icloud3/support/start_ic3_control.py | 58 ++--- .../icloud3/support/stationary_zone.py | 61 +++-- custom_components/icloud3/support/waze.py | 2 +- custom_components/icloud3/zone.py | 10 +- 16 files changed, 257 insertions(+), 237 deletions(-) diff --git a/custom_components/icloud3/ChangeLog.txt b/custom_components/icloud3/ChangeLog.txt index 317cab6..9e20f06 100644 --- a/custom_components/icloud3/ChangeLog.txt +++ b/custom_components/icloud3/ChangeLog.txt @@ -1,7 +1,13 @@ -pr1.5 +rc6 - Release Candidate 6 (10/7/2023) ............................... 1. Bug Fix - Fixed the error causing the "AttributeError: 'iCloud3_Device' object has no attribute 'interval_secs' message at line 680, in determine_interval_after_error" error. -2. China Users - Added an option to the Configure Settings - iCloud Account & iOS App Data Sources screen to enable the '.cn. Apple Web Server URL address suffix +2. China Users - Added an option to the Configure Settings - iCloud Account & iOS App Data Sources screen to enable the '.cn. Apple Web Server URL address suffix. +3. Monitored Devices - The iOSApp state value is no longer displayed on the Event Log if it is different than the iCloud3 zone and it is earlier the last FamShr update time since it is old and is probably incorrect. +4. iCloud3 Version - The version that is currently running is now displayed in the Configure Settings menu screen and more clearly on Event Log messages during startup. +5. Battery - HA has changed the battery 'state class' internal value from 'battery' to 'measurement'. +6. Prerelease Version number - This has changed to Release Candidate number to better conform to HA standards. Version pr1.5 was a developer test version and not released. +7. Configure Setting - Made some minor menu text changes for consistency. + pr1.4 diff --git a/custom_components/icloud3/__init__.py b/custom_components/icloud3/__init__.py index f5acafc..5fae6f4 100644 --- a/custom_components/icloud3/__init__.py +++ b/custom_components/icloud3/__init__.py @@ -72,7 +72,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: config_file.load_storage_icloud3_configuration_file() if Gb.conf_profile[CONF_VERSION] == 1: - _HA_LOGGER.info(f"Initializing iCloud3 {VERSION} - Remove Platform: iCloud3 statement") + _HA_LOGGER.info(f"Initializing iCloud3 v{VERSION} - Remove Platform: iCloud3 statement") except Exception as err: log_exception(err) @@ -91,9 +91,9 @@ async def start_icloud3(event=None): icloud3_started = await Gb.hass.async_add_executor_job(Gb.iCloud3.start_icloud3) if icloud3_started: - log_info_msg(f"iCloud3 {Gb.version} started") + log_info_msg(f"iCloud3 v{Gb.version} started") else: - log_error_msg(f"iCloud3 {Gb.version} Initialization Failed") + log_error_msg(f"iCloud3 v{Gb.version} Initialization Failed") #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><><><><><><><><><> # @@ -140,8 +140,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): Gb.evlog_btnconfig_url = Gb.conf_profile[CONF_EVLOG_BTNCONFIG_URL].strip() Gb.evlog_version = Gb.conf_profile['event_log_version'] - Gb.EvLog = event_log.EventLog(Gb.hass) - log_info_msg(f"Setting up iCloud3 {VERSION} - Using Integration method") + Gb.EvLog = event_log.EventLog(Gb.hass) + log_info_msg(f"Setting up iCloud3 v{VERSION} - Using Integration method") Gb.start_icloud3_inprocess_flag = True @@ -185,8 +185,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # SETUP PROCESS TO START ICLOUD3 # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - Gb.EvLog.display_user_message('iCloud3 is Starting') - Gb.EvLog.post_event(f"{EVLOG_IC3_STARTING}Initializing iCloud3 v{Gb.version} > " + Gb.EvLog.display_user_message(f"Starting iCloud3 v{Gb.version}") + Gb.EvLog.post_event(f"{EVLOG_IC3_STARTING}iCloud3 v{Gb.version} > Starting, " f"{dt_util.now().strftime('%A, %b %d')}") Gb.iCloud3 = iCloud3() @@ -210,9 +210,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): icloud3_started = await Gb.hass.async_add_executor_job(Gb.iCloud3.start_icloud3) if icloud3_started: - log_info_msg(f"iCloud3 {Gb.version} started") + log_info_msg(f"iCloud3 v{Gb.version} started") else: - log_error_msg(f"iCloud3 {Gb.version} Initialization Failed") + log_error_msg(f"iCloud3 v{Gb.version} Initialization Failed") return True diff --git a/custom_components/icloud3/config_flow.py b/custom_components/icloud3/config_flow.py index 87d8921..833b629 100644 --- a/custom_components/icloud3/config_flow.py +++ b/custom_components/icloud3/config_flow.py @@ -106,24 +106,24 @@ def dict_value_to_list(key_value_dict): return value_list #----------------------------------------------------------------------------------------- MENU_KEY_TEXT = { - 'icloud_account': 'iCLOUD ACCOUNT & iOS APP > ⠤iCloud Account Username/Password, ⠤Location Data Sources', - 'device_list': 'ICLOUD3 DEVICES > ⠤Add, Change and Delete tracked and monitored devices', - 'verification_code': 'ENTER/REQUEST AN APPLE ID VERIFICATION CODE > ⠤Enter (or Request) the 6-digit Apple ID Verification Code', - 'change_device_order': 'CHANGE DEVICE ORDER > ⠤Change the tracking order of the Devices and their display sequence on the Event Log', - 'sensors': 'SENSORS > ⠤Sensors created by iCloud3, ⠤Exclude Specific Sensors from being created', - 'actions': 'ACTION COMMANDS > ⠤Restart/Pause/Resume Polling, ⠤Debug Logging, ⠤Export Event Log, ⠤Waze Utilities', - - 'format_settings': 'FORMAT SETTINGS > ⠤Log Level, ⠤Zones Display Format, ⠤DeviceTracker State, ⠤Unit of Measure, ⠤Time & Distance, ⠤Display GPS Coordinates', - 'display_text_as': 'DISPLAY TEXT AS > ⠤Event Log Custom Card Text Replacement', - 'waze': 'WAZE ROUTE DISTANCE & TIME, WAZE HISTORY DATABASE > ⠤Route Server Location, ⠤Min/Max Intervals, ⠤Waze History Database Parameters and Controls', - 'inzone_intervals': 'INZONE DEFAULT INTERVALS > ⠤Default inZone intervals for different device types, ⠤inZone Interval if the iOS App is not installed, ⠤Other inZone Controls ', - 'special_zones': 'SPECIAL ZONES - ⠤Enter Zone Delay, ⠤Stationary Zone, ⠤Track From Zone', - 'tracking_parameters': 'TRACKING & OTHER PARAMETERS > ⠤Use Nearby Device Info, ⠤Accuracy Thresholds, ⠤Maximum, Device Offline & Other Intervals, ⠤Event Log Custom Card Directory, etc.', - - 'select': 'SELECT > Select the parameter update form', - 'next_page_0': 'PAGE 1 > DEVICES & SENSORS > ⠤iCloud Account & iOS App, ⠤iCloud3 Devices, ⠤Enter/Request Verification Code, ⠤Change Device Order, ⠤Sensors, ⠤Action Commands', - 'next_page_1': 'PAGE 2 > GENERAL PARAMETERS > ⠤Format Parameters, ⠤Display Text As, ⠤Waze Route Dist & Time, Waze History, ⠤inZone Intervals, ⠤Special Zones, ⠤ Other Parameters', - 'exit': 'EXIT AND RESTART ICLOUD3' + 'icloud_account': 'iCLOUD ACCOUNT & iOS APP ᐳ –Set iCloud Account Username/Password, –Set Location Data Sources', + 'device_list': 'ICLOUD3 DEVICES ᐳ –Add, Change and Delete tracked and monitored devices', + 'verification_code': 'ENTER/REQUEST AN APPLE ID VERIFICATION CODE ᐳ –Enter or Request the 6-digit Apple ID Verification Code', + 'change_device_order': 'CHANGE DEVICE ORDER ᐳ –Change the tracking order of the Devices and their display sequence on the Event Log', + 'sensors': 'SENSORS ᐳ –Set Sensors created by iCloud3, –Exclude Specific Sensors from being created', + 'actions': 'ACTION COMMANDS ᐳ –Restart/Pause/Resume Polling, –Debug Logging, –Export Event Log, –Waze Utilities', + + 'format_settings': 'FORMAT SETTINGS ᐳ –Log Level, –Zones Display Format, –DeviceTracker State, –Unit of Measure, –Time & Distance, Display GPS Coordinates', + 'display_text_as': 'DISPLAY TEXT AS ᐳ –Event Log Custom Card Text Replacement', + 'waze': 'WAZE ROUTE DISTANCE, TIME & HISTORY ᐳ –Set Route Server Location, Min/Max Intervals, etc. –Set Waze History Database Parameters and Controls', + 'inzone_intervals': 'INZONE DEFAULT INTERVALS ᐳ –Default inZone intervals for different device types and the iOS App, –Other inZone Controls ', + 'special_zones': 'SPECIAL ZONES ᐳ –Enter Zone Delay, –Stationary Zone, –Track From Zone', + 'tracking_parameters': 'TRACKING & OTHER PARAMETERS ᐳ –Set Nearby Device Info, Accuracy Thresholds & Other Location Request Intervals, –Set Event Log Custom Card Directory, etc.', + + 'select': 'SELECT ᐳ Select the parameter update form', + 'next_page_0': 'PAGE 1, DEVICES & SENSORS ᐳ –iCloud Account & iOS App, –iCloud3 Devices, –Enter & Request Verification Code, –Change Device Order, –Sensors, –Action Commands', + 'next_page_1': 'PAGE 2, GENERAL PARAMETERS ᐳ –Format Parameters, –Display Text As, –Waze Route Distance, Time & History, –inZone Intervals, –Special Zones, – Other Parameters', + 'exit': f'EXIT AND RESTART ICLOUD3 {".. "*22}(Version: {Gb.version})' } MENU_PAGE_0_INITIAL_ITEM = 1 MENU_KEY_TEXT_PAGE_0 = [ @@ -150,52 +150,52 @@ def dict_value_to_list(key_value_dict): ] ACTION_LIST_ITEMS_KEY_TEXT = { - 'next_page_items': 'NEXT PAGE ITEMS > ^info_field^', - 'next_page': 'NEXT PAGE > Save changes. Display the next page', - 'next_page_device': 'NEXT PAGE > Friendly Name, Track-from-Zones, Other Setup Fields', - 'next_page_waze': 'NEXT PAGE > Waze History Database parameters', - 'select_form': 'SELECT > Select the parameter update form', + 'next_page_items': 'NEXT PAGE ITEMS ᐳ ^info_field^', + 'next_page': 'NEXT PAGE ᐳ Save changes. Display the next page', + 'next_page_device': 'NEXT PAGE ᐳ Friendly Name, Track-from-Zones, Other Setup Fields', + 'next_page_waze': 'NEXT PAGE ᐳ Waze History Database parameters', + 'select_form': 'SELECT ᐳ Select the parameter update form', - 'login_icloud_account': 'LOGIN > Log into the iCloud Account. StatusMsg', - 'verification_code': 'ENTER/REQUEST AN APPLE ID VERIFICATION CODE > Enter (or Request) the 6-digit Apple ID Verification Code', + 'login_icloud_account': 'LOGIN ᐳ Log into the iCloud Account. StatusMsg', + 'verification_code': 'ENTER/REQUEST AN APPLE ID VERIFICATION CODE ᐳ Enter (or Request) the 6-digit Apple ID Verification Code', - 'enter_verification_code': 'ENTER APPLE ID VERIFICATION CODE > Enter the 6-digit Apple ID Verification Code', - 'send_verification_code': 'SEND THE VERIFICATION CODE TO APPLE ID > Send the 6-digit Apple ID Verification Code back to Apple to approve access to the iCloud account', - "request_verification_code":'REQUEST A NEW APPLE ID VERIFICATION CODE > Reset iCloud Interface and request a new Apple ID Verification Code', - 'cancel_verification_entry':'CANCEL > Cancel the Verification Code Entry and Close this screen', + 'enter_verification_code': 'ENTER APPLE ID VERIFICATION CODE ᐳ Enter the 6-digit Apple ID Verification Code', + 'send_verification_code': 'SEND THE VERIFICATION CODE TO APPLE ID ᐳ Send the 6-digit Apple ID Verification Code back to Apple to approve access to the iCloud account', + "request_verification_code":'REQUEST A NEW APPLE ID VERIFICATION CODE ᐳ Reset iCloud Interface and request a new Apple ID Verification Code', + 'cancel_verification_entry':'CANCEL ᐳ Cancel the Verification Code Entry and Close this screen', - 'update_device': 'UPDATE DEVICE > Update the selected device', - '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 FamShr/FmF/iOS App selection fields', - 'change_device_order': 'CHANGE DEVICE ORDER > Change the tracking order of the Devices and their display sequence on the Event Log', + 'update_device': 'UPDATE DEVICE ᐳ Update the selected device', + '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 FamShr/FmF/iOS App selection fields', + '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 from the iCloud3 tracked devices list', - 'delete_all_devices': 'DELETE ALL DEVICES > Delete all devices from the iCloud3 tracked devices list', - 'delete_icloud_iosapp_info':'CLEAR FAMSHR/FMF/IOSAPP INFO > Reset the FamShr/FmF/iOS App seletion fields on all devices', - 'delete_device_cancel': 'CANCEL > Return to the Device List screen', + 'delete_this_device': 'DELETE THIS DEVICE ᐳ Delete this device from the iCloud3 tracked devices list', + 'delete_all_devices': 'DELETE ALL DEVICES ᐳ Delete all devices from the iCloud3 tracked devices list', + 'delete_icloud_iosapp_info':'CLEAR FAMSHR/FMF/IOSAPP INFO ᐳ Reset the FamShr/FmF/iOS App seletion fields on all devices', + '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\'', - 'inactive_keep_inactive': 'DO NOT TRACK, KEEP INACTIVE > None of these devices should be \'Tracked\' and should remain \'Inactive\'', + 'inactive_to_track': 'TRACK ALL OR SELECTED ᐳ Change the \'Tracking Mode\' of all of the devices (or the selected devices) from \'Inactive\' to \Tracked\'', + 'inactive_keep_inactive': 'DO NOT TRACK, KEEP INACTIVE ᐳ None of these devices should be \'Tracked\' and should remain \'Inactive\'', - 'restart_ha': 'RESTART HOME ASSISTANT > Restart HA and reload iCloud3', - 'restart_ic3_now': 'RESTART NOW > Restart iCloud3 now to load the updated configuration', - 'restart_ic3_later': 'RESTART LATER > The configuration changes have been saved. Load the updated configuration the next time iCloud3 is started', - 'reload_icloud3': 'RELOAD ICLOUD3 > Reload & Restart iCloud3 (EXPERIMENTAL: THIS MAY NOT WORK)', - 'review_inactive_devices': 'REVIEW INACTIVE DEVICES > Some Devices are `Inactive` and will not be located or tracked', + 'restart_ha': 'RESTART HOME ASSISTANT ᐳ Restart HA and reload iCloud3', + 'restart_ic3_now': 'RESTART NOW ᐳ Restart iCloud3 now to load the updated configuration', + 'restart_ic3_later': 'RESTART LATER ᐳ The configuration changes have been saved. Load the updated configuration the next time iCloud3 is started', + 'reload_icloud3': 'RELOAD ICLOUD3 ᐳ Reload & Restart iCloud3 (EXPERIMENTAL: THIS MAY NOT WORK)', + 'review_inactive_devices': 'REVIEW INACTIVE DEVICES ᐳ Some Devices are `Inactive` and will not be located or tracked', - 'select_text_as': 'SELECT > Update selected \'Display Text As\' field', - 'clear_text_as': 'CLEAR > Remove \'Display Test As\' entry', + 'select_text_as': 'SELECT ᐳ Update selected \'Display Text As\' field', + 'clear_text_as': 'CLEAR ᐳ Remove \'Display Test As\' entry', - 'exclude_sensors': 'EXCLUDE SENSORS > Select specific Sensors that should not be created', - 'filter_sensors': 'FILTER SENSORS > Select Sensors that should be displayed', + 'exclude_sensors': 'EXCLUDE SENSORS ᐳ Select specific Sensors that should not be created', + 'filter_sensors': 'FILTER SENSORS ᐳ Select Sensors that should be displayed', - 'move_up': 'MOVE UP > Move the Device up in the list', - 'move_down': 'MOVE DOWN > Move the Device down in the list', + 'move_up': 'MOVE UP ᐳ Move the Device up in the list', + 'move_down': 'MOVE DOWN ᐳ Move the Device down in the list', - 'save': 'SAVE > Update Configuration File, Return to the menu screen', - 'cancel': 'RETURN > Return to the previous screen. Cancel any changes that are not already saved', - 'exit': 'EXIT > Exit the iCloud3 Configurator', - 'return': 'RETURN > Return to the Main Menu', + 'save': 'SAVE ᐳ Update Configuration File, Return to the menu screen', + 'cancel': 'RETURN ᐳ Return to the previous screen. Cancel any changes that are not already saved', + 'exit': 'EXIT ᐳ Exit the iCloud3 Configurator', + 'return': 'RETURN ᐳ Return to the Main Menu', "divider1": "═══════════════════════════════════════", "divider2": "═══════════════════════════════════════", @@ -274,9 +274,9 @@ def dict_value_to_list(key_value_dict): 'none': 'Use normal Apple iCloud Servers', 'cn': 'China - Use Apple iCloud Servers located in China' } -IOSAPP_DEVICE_SEARCH_TEXT = 'Scan for mobile app device_tracker > ' +IOSAPP_DEVICE_SEARCH_TEXT = 'Scan for mobile app device_tracker ᐳ ' IOSAPP_DEVICE_NONE_ITEMS_KEY_TEXT = { - 'None': 'None > The iOS App is not installed on this device', + 'None': 'None - The iOS App is not installed on this device', } TRACKING_MODE_ITEMS_KEY_TEXT = { 'track': 'Track - Request Location and track the device', @@ -327,80 +327,80 @@ def dict_value_to_list(key_value_dict): } WAZE_HISTORY_TRACK_DIRECTION_ITEMS_KEY_TEXT = { 'north_south': 'North-South - You generally travel in North-to-South direction', - 'east_west': 'East-West- You generally travel in East-West direction' + 'east_west': 'East-West - You generally travel in East-West direction' } CONF_SENSORS_MONITORED_DEVICES_KEY_TEXT = { - 'md_badge': 'badge > Badge sensor > A badge showing the Zone Name or distance from the Home zone. Attributes include location related information', - 'md_battery': 'battery, battery_status > Create Battery (65%) and Battery Status (Charging, Low, etc) sensors', - 'md_location_sensors': 'Location related sensors > Name, zone, distance, travel_time, etc. (_name, _zone, _zone_fname, _zone_name, _zone_datetime, _home_distance, _travel_time, _travel_time_min, _last_located, _last_update)', + 'md_badge': '_badge ᐳ Badge sensor - A badge showing the Zone Name or distance from the Home zone. Attributes include location related information', + 'md_battery': '_battery, battery_status ᐳ Create Battery (65%) and Battery Status (Charging, Low, etc) sensors', + 'md_location_sensors': 'Location related sensors ᐳ Name, zone, distance, travel_time, etc. (_name, _zone, _zone_fname, _zone_name, _zone_datetime, _home_distance, _travel_time, _travel_time_min, _last_located, _last_update)', } CONF_SENSORS_DEVICE_KEY_TEXT = { - NAME: 'name > iCloud3 Device Name', - 'badge': 'badge > A badge showing the Zone Name or distance from the Home zone', - BATTERY: 'battery, battery_status > Create Battery Level (65%) and Battery Status (Charging, Low, etc) sensors', - 'info': 'info > An information message containing status, alerts and errors related to device location updates, data accuracy, etc', + NAME: '_name ᐳ iCloud3 Device Name', + 'badge': '_badge ᐳ A badge showing the Zone Name or distance from the Home zone', + BATTERY: '_battery, _battery_status ᐳ Create Battery Level (65%) and Battery Status (Charging, Low, etc) sensors', + 'info': '_info ᐳ An information message containing status, alerts and errors related to device location updates, data accuracy, etc', } CONF_SENSORS_TRACKING_UPDATE_KEY_TEXT = { - 'interval': 'interval > Time between location requests', - 'last_update': 'last_update > Last time the location was updated', - 'next_update': 'next_update > Next time the location will be updated', - 'last_located': 'last_located > Last time the was located using iCloud or iOS APP location', + 'interval': '_interval ᐳ Time between location requests', + 'last_update': '_last_update ᐳ Last time the location was updated', + 'next_update': '_next_update ᐳ Next time the location will be updated', + 'last_located': '_last_located ᐳ Last time the was located using iCloud or iOS APP location', } CONF_SENSORS_TRACKING_TIME_KEY_TEXT = { - 'travel_time': 'travel_time > Waze Travel time to Home or closest Track-from-Zone zone', - 'travel_time_min': 'travel_time_min > Waze Travel time to Home or closest Track-from-Zone zone in minutes', - 'travel_time_hhmm': 'travel_time_hhmm > Waze Travel time to a Zone in hours:minutes', - 'arrival_time': 'arrival_time > Home Zone arrival time based on Waze Travel time', + 'travel_time': '_travel_time ᐳ Waze Travel time to Home or closest Track-from-Zone zone', + 'travel_time_min': '_travel_time_min ᐳ Waze Travel time to Home or closest Track-from-Zone zone in minutes', + 'travel_time_hhmm': '_travel_time_hhmm ᐳ Waze Travel time to a Zone in hours:minutes', + 'arrival_time': '_arrival_time ᐳ Home Zone arrival time based on Waze Travel time', } CONF_SENSORS_TRACKING_DISTANCE_KEY_TEXT = { - 'home_distance': 'home_distance > Distance to the Home zone', - 'zone_distance': 'zone_distance > Distance to the Home or closest Track-from-Zone zone', - 'dir_of_travel': 'dir_of_travel > Direction of Travel for the Home zone or closest Track-from-Zone zone (Towards, AwayFrom, inZone, etc)', - 'moved_distance': 'moved_distance > Distance moved from the last location', + 'home_distance': '_home_distance ᐳ Distance to the Home zone', + 'zone_distance': '_zone_distance ᐳ Distance to the Home or closest Track-from-Zone zone', + 'dir_of_travel': '_dir_of_travel ᐳ Direction of Travel for the Home zone or closest Track-from-Zone zone (Towards, AwayFrom, inZone, etc)', + 'moved_distance': '_moved_distance ᐳ Distance moved from the last location', } CONF_SENSORS_TRACK_FROM_ZONES_KEY_TEXT = { - 'general_sensors': 'Include General Sensors (zone_info)', - 'time_sensors': 'Include Travel Time Sensors (travel_time, travel_time_mins, travel_time_hhmm, arrival_time', - 'distance_sensors': 'Include Zone Distance Sensors (zone_distance, distance, dir_of_travel)', + 'general_sensors': 'Include General Sensors (_zone_info)', + 'time_sensors': 'Include Travel Time Sensors (_travel_time, _travel_time_mins, _travel_time_hhmm, _arrival_time', + 'distance_sensors': 'Include Zone Distance Sensors (_zone_distance, _distance, _dir_of_travel)', } CONF_SENSORS_TRACK_FROM_ZONES_KEYS = ['general_sensors', 'time_sensors', 'distance_sensors'] CONF_SENSORS_TRACKING_OTHER_KEY_TEXT = { - 'trigger': 'trigger > Last action that triggered a location update', - 'waze_distance': 'waze_distance > Waze distance from a TrackFrom zone', - 'calc_distance': 'calc_distance > Calculated straight line distance from a TrackFrom zone', + 'trigger': '_trigger ᐳ Last action that triggered a location update', + 'waze_distance': '_waze_distance ᐳ Waze distance from a TrackFrom zone', + 'calc_distance': '_calc_distance ᐳ Calculated straight line distance from a TrackFrom zone', } CONF_SENSORS_ZONE_KEY_TEXT = { - 'zone_fname': 'zone_fname > HA Zone entity Friendly Name (HA Config > Areas & Zones > Zones > Name)', - 'zone': 'zone > HA Zone entity_id (`the_shores`)', - 'zone_name': 'zone_name > Reformat the Zone entity_id, capitalize and remove `_`s (`the_shores` → `TheShores`)', - 'zone_datetime': 'zone_datetime > The time the Device entered the Zone', - 'last_zone': 'last_zone_[...] > Create the same sensors for the device`s last HA Zone', + 'zone_fname': '_zone_fname ᐳ HA Zone entity Friendly Name (HA Config > Areas & Zones > Zones > Name)', + 'zone': '_zone ᐳ HA Zone entity_id (`the_shores`)', + 'zone_name': '_zone_name ᐳ Reformat the Zone entity_id, capitalize and remove `_`s (`the_shores` → `TheShores`)', + 'zone_datetime': '_zone_datetime ᐳ The time the Device entered the Zone', + 'last_zone': '_last_zone_[...] ᐳ Create the same sensors for the device`s last HA Zone', } CONF_SENSORS_OTHER_KEY_TEXT = { - 'gps_accuracy': 'gps_accuracy > GPS acuracy of the last location coordinates', - 'vertical_accuracy':'vertical_accuracy > Vertical (Elevation) Accuracy', - 'altitude': 'altitude > Altitude/Elevation', + 'gps_accuracy': '_gps_accuracy ᐳ GPS acuracy of the last location coordinates', + 'vertical_accuracy':'_vertical_accuracy ᐳ Vertical (Elevation) Accuracy', + 'altitude': '_altitude ᐳ Altitude/Elevation', } ACTIONS_SCREEN_ITEMS_KEY_TEXT = { "divider1": "═════════════ ICLOUD3 CONTROL ACTIONS ══════════════", - "restart": "RESTART > Restart iCloud3", - "pause": "PAUSE > Pause polling on all devices", - "resume": "RESUME > Resume Polling on all devices, Refresh all locations", + "restart": "RESTART ᐳ Restart iCloud3", + "pause": "PAUSE ᐳ Pause polling on all devices", + "resume": "RESUME ᐳ Resume Polling on all devices, Refresh all locations", "divider2": "════════════════ DEBUG LOG ACTIONS ══════════════", - "debug_start": "START DEBUG LOGGING > Start or stop debug logging", - "debug_stop": "STOP DEBUG LOGGING > Start or stop debug logging", - "rawdata_start": "START RAWDATA LOGGING > Start or stop debug rawdata logging", - "rawdata_stop": "STOP RAWDATA LOGGING > Start or stop debug rawdata logging", - "commit": "COMMIT DEBUG LOG RECORDS > Verify all debug log file records are written", + "debug_start": "START DEBUG LOGGING ᐳ Start or stop debug logging", + "debug_stop": "STOP DEBUG LOGGING ᐳ Start or stop debug logging", + "rawdata_start": "START RAWDATA LOGGING ᐳ Start or stop debug rawdata logging", + "rawdata_stop": "STOP RAWDATA LOGGING ᐳ Start or stop debug rawdata logging", + "commit": "COMMIT DEBUG LOG RECORDS ᐳ Verify all debug log file records are written", "divider3": "════════════════ OTHER COMMANDS ═══════════════", - "evlog_export": "EXPORT EVENT LOG > Export Event Log data", - "wazehist_maint": "WAZE HIST DATABASE > Recalc time/distance data at midnight", - "wazehist_track": "WAZE HIST MAP TRACK > Load route locations for map display", + "evlog_export": "EXPORT EVENT LOG ᐳ Export Event Log data", + "wazehist_maint": "WAZE HIST DATABASE ᐳ Recalc time/distance data at midnight", + "wazehist_track": "WAZE HIST MAP TRACK ᐳ Load route locations for map display", "divider4": "═══════════════════════════════════════════════", - "restart_ha": "RESTART HA, RELOAD ICLOUD3 > Restart HA or Reload iCloud3", - "return": "MAIN MENU > Return to the Main Menu" + "restart_ha": "RESTART HA, RELOAD ICLOUD3 ᐳ Restart HA or Reload iCloud3", + "return": "MAIN MENU ᐳ Return to the Main Menu" } ACTIONS_SCREEN_ITEMS_TEXT = [text for text in ACTIONS_SCREEN_ITEMS_KEY_TEXT.values()] ACTIONS_SCREEN_ITEMS_KEY_BY_TEXT = {text: key @@ -1283,6 +1283,8 @@ async def async_step_sensors(self, user_input=None, errors=None): if HOME_DISTANCE not in user_input[CONF_SENSORS_TRACKING_DISTANCE]: user_input[CONF_SENSORS_TRACKING_DISTANCE].append(HOME_DISTANCE) + # TfZ Sensors are not configured via config_flow but built in + # config_flow from the distance, time & zone sensors tfz_sensors_base = ['zone_info'] tfz_sensors_base.extend(user_input[CONF_SENSORS_TRACKING_TIME]) tfz_sensors_base.extend(user_input[CONF_SENSORS_TRACKING_DISTANCE]) diff --git a/custom_components/icloud3/const.py b/custom_components/icloud3/const.py index 106e961..2842e60 100644 --- a/custom_components/icloud3/const.py +++ b/custom_components/icloud3/const.py @@ -4,7 +4,7 @@ # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -VERSION = '3.0.pr1.5' +VERSION = '3.0.rc6' DOMAIN = 'icloud3' ICLOUD3 = 'iCloud3' @@ -245,7 +245,7 @@ lite_circled_letters = "Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ" dark_circled_letters = "🅐 🅑 🅒 🅓 🅔 🅕 🅖 🅗 🅘 🅙 🅚 🅛 🅜 🅝 🅞 🅟 🅠 🅡 🅢 🅣 🅤 🅥 🅦 🅧 🅨 🅩 ✪" Symbols = ±▪•●▬⮾ ⊗ ⊘✓×ø¦ ▶◀ ►◄▲▼ ∙▪ »« oPhone=►▶→⟾➤➟➜➔➤🡆🡪🡺⟹🡆➔ᐅ◈🝱☒☢⛒❌⊘Ɵ⊗ⓧⓍ⛒🜔 - — – ⁃ » ━▶ ━➤🡺 —> > > ❯↦ … 🡪ᗕ ᗒ ᐳ ─🡢 ──ᗒ 🡢 ─ᐅ ↣ ➙ →《》◆◈◉●▐‖ ▹▻▷◁◅◃▶➤➜➔❰❰❱❱ ⠤ + — –ᗒ ⁃ » ━▶ ━➤🡺 —> > > ❯↦ … 🡪ᗕ ᗒ ᐳ ─🡢 ──ᗒ 🡢 ─ᐅ ↣ ➙ →《》◆◈◉●▐‖ ▹▻▷◁◅◃▶➤➜➔❰❰❱❱ ⠤ ⣇⠈⠉⠋⠛⠟⠿⡿⣿ https://www.fileformat.info/info/unicode/block/braille_patterns/utf8test.htm ''' NBSP = '⠈' #' ' @@ -703,6 +703,8 @@ MOVED_TIME_FROM = 'moved_from' MOVED_TIME_TO = 'moved_to' +# TfZ Sensors are not configured via config_flow but built in +# config_flow from the distance, time & zone sensors CONF_SENSORS_TRACK_FROM_ZONES = 'track_from_zones' TFZ_ZONE_INFO = 'tfz_zone_info' TFZ_DISTANCE = 'tfz_distance' diff --git a/custom_components/icloud3/device.py b/custom_components/icloud3/device.py index a32d0c2..605263e 100644 --- a/custom_components/icloud3/device.py +++ b/custom_components/icloud3/device.py @@ -2066,6 +2066,11 @@ def format_info_msg(self): # if self.dev_data_battery_level > 0: # info_msg += f"Battery-{self.format_battery_level}, " + if (self.iosapp_monitor_flag + and secs_since(self.iosapp_data_secs) > 3600): + info_msg += (f"iOSApp LastUpdate-" + f"{secs_to_age_str(self.iosapp_data_secs)}, ") + if self.is_gps_poor: info_msg += (f"PoorGPS-±{self.loc_data_gps_accuracy}m " f"#{self.old_loc_poor_gps_cnt}") @@ -2082,7 +2087,7 @@ def format_info_msg(self): except Exception as err: log_exception(err) - self.info_msg = 'iCloud3 Started' if info_msg == '' else info_msg[:-2] + self.info_msg = f'iCloud3 v{Gb.version} > Started' if info_msg == '' else info_msg[:-2] return self.info_msg #------------------------------------------------------------------- diff --git a/custom_components/icloud3/global_variables.py b/custom_components/icloud3/global_variables.py index cd75252..6367354 100644 --- a/custom_components/icloud3/global_variables.py +++ b/custom_components/icloud3/global_variables.py @@ -138,6 +138,7 @@ class GlobalVariables(object): TrackedZones_by_zone = {HOME, None} # Tracked zones object by zone name set up with Devices.DeviceFmZones object ActiveZones = [] # Active Zones - Not passive, radius > 0 StatZones = [] # Stationary Zone objects + StatZones_to_delete = [] # Stationary Zone to delete after the devices that we're in it have been updated StatZones_by_zone = {} # Stationary Zone objects by their id number (1-10 --> ic3_#_stationary) HomeZone = None # Home Zone object diff --git a/custom_components/icloud3/icloud3_main.py b/custom_components/icloud3/icloud3_main.py index e355178..ea88c84 100644 --- a/custom_components/icloud3/icloud3_main.py +++ b/custom_components/icloud3/icloud3_main.py @@ -161,6 +161,7 @@ def start_icloud3(self): start_ic3_control.stage_3_setup_configured_devices() stage_4_success = start_ic3_control.stage_4_setup_data_sources() + if stage_4_success is False: start_ic3_control.stage_4_setup_data_sources(retry=True) @@ -307,7 +308,7 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): Gb.trace_prefix = 'WRAPUP > ' #<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> - # UPDATE DISPLAYED DEVICE INFO FIELD + # POST UPDATE LOOP TASKS #<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> # Update the EvLog display if the displayed device was updated after # the last EvLog refresh @@ -315,11 +316,7 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): if Device := Gb.Devices_by_devicename.get(Gb.EvLog.devicename): if Device.last_evlog_msg_secs > Gb.EvLog.last_refresh_secs: Gb.EvLog.update_event_log_display(devicename=Device.devicename) - # show_one_screen=show_one_screen) - #<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> - # UPDATE DISTANCE TO DEVICES SENSORS - #<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>> # Update distance sensors (_zone/home.waze/calc_distace) to update the # distance to each device if Gb.dist_to_other_devices_update_sensor_list: @@ -331,6 +328,14 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): Gb.dist_to_other_devices_update_sensor_list = set() + # Remove all StatZones from HA flagged for removal in StatZone module + # Removing them after all devices have been updated lets HA process the statzone 'leave' + # automation trigger associated with a device before the zone is deleted. + if Gb.StatZones_to_delete: + for StatZone in Gb.StatZones_to_delete: + StatZone.remove_ha_zone() + Gb.StatZones_to_delete = [] + Gb.trace_prefix = '' @@ -339,7 +344,6 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): # MAIN 5-SEC LOOP PROCESSING CONTROLLERS # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - def _main_5sec_loop_update_tracked_devices_iosapp(self, Device): ''' Update the device based on iOS App data @@ -365,11 +369,6 @@ def _main_5sec_loop_update_tracked_devices_iosapp(self, Device): post_event(devicename, event_msg) return - # xxxxx - # if Device.isnot_set is False: - # Device.moved_since_last_update_km = \ - # Device.distance_km(Device.iosapp_data_latitude, Device.iosapp_data_longitude) - # If the iOS App is the primary data source next_update time is reached, get the # old location threshold. Send a location request to the iosapp device if the # data is older than the threshold, the next_update is newer than the iosapp data @@ -425,22 +424,12 @@ def _main_5sec_loop_update_tracked_devices_iosapp(self, Device): and is_statzone(Device.iosapp_zone_exit_zone) and Device.StatZone and Device.iosapp_zone_exit_dist_m < Device.StatZone.radius_m): - # Device.iosapp_data_change_reason = (f"Relocate StatZone (" - # f"{zone_display_as(Device.iosapp_zone_exit_zone)})") - # event_msg =(f"Trigger Changed > {Device.iosapp_data_change_reason}, " - # f"Distance less than zone size") - xiosapp_data_change_reason = (f"XXXXX CANCELED Relocate StatZone (" - f"{zone_display_as(Device.iosapp_zone_exit_zone)})") + event_msg =(f"{EVLOG_ALERT}Trigger Changed > {xiosapp_data_change_reason}, " f"Distance less than zone size " f"{Device.StatZone.display_as} {Device.iosapp_zone_exit_dist_m} < {Device.StatZone.radius_m}") post_event(devicename, event_msg) - - # statzone.move_statzone_to_device_location(Device, - # latitude=Device.loc_data_latitude, - # longitude=Device.loc_data_longitude) - # return statzone.kill_and_recreate_unuseable_statzone(Device) else: @@ -545,6 +534,9 @@ def _main_5sec_loop_update_monitored_devices(self, Device): if Device.loc_data_dist_moved_km < .05: return + if Device.loc_data_latitude == 0: + return + Device.update_sensors_flag = True Device.icloud_update_reason = 'Monitored Device Update' @@ -1012,8 +1004,8 @@ def _determine_interval_and_next_update(self, Device): det_interval.determine_TrackFrom_zone(Device) # pr1.4 - # If the location is old and an update it's being done (probably from an iosapp trigger), - # see if the error interval is greater than this update interval. Is it is, Reset the counter + # If the location is old and an update is being done (probably from an iosapp trigger), + # see if the error interval is greater than this update interval. Is it is, Reset the counter if Device.old_loc_poor_gps_cnt > 8: error_interval_secs, error_cnt, max_error_cnt = det_interval.get_error_retry_interval(Device) if error_interval_secs > Device.interval_secs: diff --git a/custom_components/icloud3/sensor.py b/custom_components/icloud3/sensor.py index 6c249bc..0c57a85 100644 --- a/custom_components/icloud3/sensor.py +++ b/custom_components/icloud3/sensor.py @@ -245,7 +245,6 @@ def _create_track_from_zone_sensors(devicename, conf_device, sensors_list): devicename_sensor_zone = f"{devicename}_{sensor}_{from_zone}" if devicename_sensor_zone in excluded_sensors_list: - # Gb.sensors_created_cnt += 1 log_debug_msg(f"Sensor entity excluded: sensor.{devicename_sensor_zone}") continue @@ -508,8 +507,6 @@ def _get_extra_attributes(self, sensor): if instr(_sensor_attr_name, ZONE_DISTANCE_M_EDGE): extra_attrs[_sensor_attr_name] = _sensor_value - # if attr_units_flag == False: - # attr_units_flag = True if Gb.um == 'mi': extra_attrs['distance_units_(attributes)'] = 'mi' if self._get_sensor_value(ZONE_DISTANCE_M): @@ -899,7 +896,7 @@ def native_value(self): return Gb.broadcast_info_msg elif self.sensor_not_set: - return f"◈◈ Starting iCloud3 ◈◈" + return f"◈◈ Starting iCloud3 v{Gb.version} ◈◈" else: return self.sensor_value @@ -1117,7 +1114,7 @@ def native_value(self): @property def extra_state_attributes(self): extra_attrs = self._get_extra_attributes(self.sensor) - extra_attrs.update({'device_class': 'battery', 'state_class': 'battery'}) + extra_attrs.update({'device_class': 'battery', 'state_class': 'measurement'}) return extra_attrs diff --git a/custom_components/icloud3/support/determine_interval.py b/custom_components/icloud3/support/determine_interval.py index 80f55dd..3dfb13e 100644 --- a/custom_components/icloud3/support/determine_interval.py +++ b/custom_components/icloud3/support/determine_interval.py @@ -323,12 +323,6 @@ def determine_interval(Device, FromZone): interval_method += '+>180s' interval_secs = 180 - #15-sec interval_secs (close to zone) and may be going into a stationary zone, - #increase the interval - # elif interval_secs == 15 and Gb.this_update_secs >= Device.statzone_timer+45: - # interval_method += '+6.StatTimer+45' - # interval_secs = 30 - #Turn off waze close to zone flag to use waze after leaving zone or getting more than 1km from it if Gb.Waze.waze_close_to_zone_pause_flag: if inzone_flag or calc_dist_from_zone_km >= 1: @@ -362,7 +356,6 @@ def determine_interval(Device, FromZone): # Use interval_secs if > StatZone interval unless the StatZone interval is the device's # inzone interval if Device.is_in_statzone: - #and Device.statzone_inzone_interval_secs >= interval_secs): interval_method = "7.StatZone" interval_secs = Device.statzone_inzone_interval_secs @@ -373,8 +366,7 @@ def determine_interval(Device, FromZone): interval_secs = Device.statzone_inzone_interval_secs elif inzone_flag: pass - # interval_method = f"7.inZoneMax" - # interval_secs = Gb.max_interval_secs / 2 + else: interval_method = f"7.Max" interval_secs = Gb.max_interval_secs @@ -395,13 +387,13 @@ def determine_interval(Device, FromZone): if (_Device.FromZone_TrackFrom and _Device.near_device_distance <= NEAR_DEVICE_DISTANCE and next_update_secs < _Device.FromZone_TrackFrom.next_update_secs): + next_update_secs = _Device.FromZone_TrackFrom.next_update_secs interval_secs = _Device.FromZone_TrackFrom.interval_secs interval_str = _Device.FromZone_TrackFrom.interval_str interval_method = f"{_Device.fname}" + except: - error_msg = f"User Monitor Results error > {_Device.devicename}, Zone-{_Device.FromZone_TrackFrom.from_zone}" - post_error_msg(error_msg) pass # Update all dates and other fields @@ -425,7 +417,6 @@ def determine_interval(Device, FromZone): except Exception as err: sensor_msg = post_internal_error('Update FromZone Times', traceback.format_exc) - #-------------------------------------------------------------------------------- # if poor gps and moved less than 1km, redisplay last distances if (Device.state_change_flag is False @@ -453,6 +444,14 @@ def determine_interval(Device, FromZone): and waze_dist_from_zone_km > FromZone.max_dist_km): FromZone.max_dist_km = waze_dist_from_zone_km + if Device.iosapp_monitor_flag: + # If monitored and the iosapp state is before the last update, reset it since + # the iosapp is not really used for monitored devices + if (Device.is_monitored + and Device.iosapp_data_state != Device.loc_data_zone + and Device.last_update_loc_secs > (Device.iosapp_data_state_secs + Device.old_loc_threshold_secs)): + Device.iosapp_data_state = NOT_SET + #-------------------------------------------------------------------------------- #Make sure the new 'last state' value is the internal value for #the state (e.g., Away-->not_home) to reduce state change triggers later. @@ -533,10 +532,15 @@ def post_results_message_to_event_log(Device, FromZone): if Device.is_monitored: event_msg += f"{Device.dev_data_source}, " if Gb.log_debug_flag and FromZone.interval_method and Device.is_tracked: - event_msg += (f"Method-{FromZone.interval_method}, " + event_msg += ( f"Method-{FromZone.interval_method}, " f"{'Went3km, ' if Device.went_3km else ''}") if Gb.Waze.waze_status == WAZE_OUT_OF_RANGE: event_msg += "Waze-OverMaxDist, " + if (Device.iosapp_monitor_flag + and secs_since(Device.iosapp_data_secs) > 3600): + event_msg += ( f"iOSAppLastUpdate-" + f"{secs_to_age_str(Device.iosapp_data_secs)}, ") + post_event(Device.devicename, event_msg[:-2]) if True is True: #Device.is_tracked: @@ -566,15 +570,8 @@ def post_zone_time_dist_event_msg(Device, FromZone): Event Log ''' - iosapp_state = '──' - if Device.iosapp_monitor_flag: - iosapp_state = zone_display_as(Device.iosapp_data_state) - if (is_statzone(Device.iosapp_data_state) - and Device.iosapp_data_state != Device.loc_data_zone - and Device.iosapp_data_state not in statzone.ha_statzones()): - iosapp_state = f"🅧{iosapp_state}" - - ic3_zone = zone_display_as(Device.loc_data_zone) + iosapp_state = zone_display_as(Device.iosapp_data_state) + ic3_zone = zone_display_as(Device.loc_data_zone) if Device.loc_data_zone == NOT_SET: interval_str = travel_time = distance = 0 diff --git a/custom_components/icloud3/support/pyicloud_ic3.py b/custom_components/icloud3/support/pyicloud_ic3.py index a8e5d20..7ee07c9 100644 --- a/custom_components/icloud3/support/pyicloud_ic3.py +++ b/custom_components/icloud3/support/pyicloud_ic3.py @@ -26,7 +26,8 @@ FMF, FAMSHR, FMF_FNAME, FAMSHR_FNAME, NAME, ID, APPLE_SPECIAL_ICLOUD_SERVER_COUNTRY_CODE, ICLOUD_HORIZONTAL_ACCURACY, - LOCATION, TIMESTAMP, LOCATION_TIME, DATA_SOURCE, + LOCATION, TIMESTAMP, LOCATION_TIME, DATA_SOURCE, + ICLOUD_BATTERY_LEVEL, ICLOUD_BATTERY_STATUS, BATTERY_STATUS_CODES, ICLOUD_DEVICE_STATUS, CONF_PASSWORD, CONF_MODEL_DISPLAY_NAME, CONF_RAW_MODEL, CONF_ICLOUD_SERVER_ENDPOINT_SUFFIX, @@ -1427,10 +1428,14 @@ def update_device_location_data(self, requested_by_devicename=None, device_data= # {'raw': _RawData.device_data}) if requested_by_prefix == '': requested_by_prefix = CRLF_DOT + rawdata_battery_level = round(_RawData.device_data.get(ICLOUD_BATTERY_LEVEL, 0) * 100) monitor_msg += (f"{requested_by_prefix}" f"{_Device.devicename}, " f"{last_loc_time_gps_msg}" - f"{_RawData.loc_time_gps}") + f"{_RawData.loc_time_gps} " + f", {rawdata_battery_level}%") + if rawdata_battery_level != _Device.dev_data_battery_level: + monitor_msg += f"/{_Device.dev_data_battery_level}%" post_monitor_msg(monitor_msg) diff --git a/custom_components/icloud3/support/service_handler.py b/custom_components/icloud3/support/service_handler.py index 2d983d3..cc907c3 100644 --- a/custom_components/icloud3/support/service_handler.py +++ b/custom_components/icloud3/support/service_handler.py @@ -491,9 +491,20 @@ def _handle_action_device_locate(Device, action_option): _handle_action_device_location_iosapp(Device) return - if Gb.primary_data_source_ICLOUD is False or Device.is_data_source_ICLOUD is False: + if (Gb.primary_data_source_ICLOUD is False + or (Device.device_id_famshr is None and Device.device_id_fmf is None) + or Device.is_offline + or Device.is_data_source_ICLOUD is False): post_event(Device.devicename, "iCloud Location Tracking is not available") return + + if Device.old_loc_poor_gps_cnt > 3: + post_event(Device.devicename, "Location request canceled. Old Location Retry is " + "handling Location Updates") + ost_event(Device.devicename, "iCloud Location Tracking is not available") + Gb.force_icloud_update_flag = False + return + try: interval_secs = time_str_to_secs(action_option) if interval_secs == 0: diff --git a/custom_components/icloud3/support/start_ic3.py b/custom_components/icloud3/support/start_ic3.py index 2f4664a..9124b92 100644 --- a/custom_components/icloud3/support/start_ic3.py +++ b/custom_components/icloud3/support/start_ic3.py @@ -337,7 +337,6 @@ def set_global_variables_from_conf_parameters(evlog_msg=True): config_event_msg += ( f"{CRLF_DOT}Set Default Tracking Method " f"({DATA_SOURCE_FNAME.get(Gb.primary_data_source, Gb.primary_data_source)})") - # log_level = 'debug' if Gb.conf_profile[CONF_VERSION] <= 0 else Gb.log_level set_log_level(Gb.log_level) config_event_msg += f"{CRLF_DOT}Initialize Debug Control ({Gb.log_level})" @@ -1944,7 +1943,7 @@ def setup_trackable_devices(): tracking_mode = '' else: Gb.reinitialize_icloud_devices_flag = (Gb.conf_famshr_device_cnt > 0) - tracking_mode = f"{CIRCLE_STAR} NOT " + tracking_mode = f"{RED_X} NOT " tracking_mode += 'Monitored' if Device.is_monitored else 'Tracked' event_msg =(f"{tracking_mode} Device > {devicename} ({Device.fname_devtype})") diff --git a/custom_components/icloud3/support/start_ic3_control.py b/custom_components/icloud3/support/start_ic3_control.py index d1fd791..1ffc114 100644 --- a/custom_components/icloud3/support/start_ic3_control.py +++ b/custom_components/icloud3/support/start_ic3_control.py @@ -27,7 +27,7 @@ def stage_1_setup_variables(): Gb.trace_prefix = 'STAGE 1 > ' - stage_title = 'Stage 1 > Initial Preparations' + stage_title = f'Stage 1 > Initial Preparations' broadcast_info_msg(stage_title) @@ -35,7 +35,7 @@ def stage_1_setup_variables(): if Gb.start_icloud3_inprocess_flag: return - Gb.EvLog.display_user_message('iCloud3 > Initializiing') + Gb.EvLog.display_user_message(f'iCloud3 v{Gb.version} > Initializiing') try: Gb.this_update_secs = time_now_secs() @@ -49,7 +49,7 @@ def stage_1_setup_variables(): Gb.reinitialize_icloud_devices_cnt = 0 if Gb.initial_icloud3_loading_flag is False: - post_event( f"{EVLOG_IC3_STARTING}Restarting iCloud3 v{Gb.version} > " + post_event( f"{EVLOG_IC3_STARTING}iCloud3 v{Gb.version} > Restarting, " f"{dt_util.now().strftime('%A, %b %d')}") # Gb.EvLog.update_event_log_display("") # start_ic3.reinitialize_config_parameters() @@ -86,7 +86,7 @@ def stage_1_setup_variables(): def stage_2_prepare_configuration(): Gb.trace_prefix = 'STAGE 2 > ' - stage_title = 'Stage 2 > Prepare Support Services' + stage_title = f'Stage 2 > Prepare Support Services' try: Gb.EvLog.display_user_message(stage_title) @@ -140,7 +140,7 @@ def stage_2_prepare_configuration(): def stage_3_setup_configured_devices(): Gb.trace_prefix = 'STAGE 3 > ' - stage_title = 'Stage 3 > Prepare Configured Devices' + stage_title = f'Stage 3 > Prepare Configured Devices' try: Gb.EvLog.display_user_message(stage_title) @@ -170,7 +170,7 @@ def stage_3_setup_configured_devices(): def stage_4_setup_data_sources(retry=False): Gb.trace_prefix = 'STAGE 4 > ' - stage_title = 'Stage 4 > Setup iCloud & iOSApp Data Source' + stage_title = f'Stage 4 > Setup iCloud & iOSApp Data Source' # Missing username/password, PyiCloud can not be started if Gb.primary_data_source_ICLOUD: @@ -215,7 +215,7 @@ def stage_4_setup_data_sources(retry=False): event_msg = 'iOS App > Not used as a data source' post_event(event_msg) - return_code = _list_unverified_devices(retry=retry) + return_code = _are_all_devices_verified(retry=retry) except Exception as err: log_exception(err) @@ -227,7 +227,7 @@ def stage_4_setup_data_sources(retry=False): return return_code #------------------------------------------------------------------ -def _list_unverified_devices(retry=False): +def _are_all_devices_verified(retry=False): ''' See if all tracked devices are verified. @@ -249,7 +249,6 @@ def _list_unverified_devices(retry=False): if (devicename in unverified_devices \ and ((Device.device_id_famshr and Gb.conf_data_source_FAMSHR) \ or (Device.device_id_fmf and Gb.conf_data_source_FMF)))] - if unverified_devices == [] or not_tracked_devices == []: return True @@ -264,7 +263,7 @@ def _list_unverified_devices(retry=False): else: event_msg = (f"{EVLOG_ALERT}ALERT > Some devices could not be verified. iCloud Location Services " f"will be reinitialized") - event_msg += (f"{CRLF}{CRLF}Unverified Devices > {', '.join(unverified_devices)}") + event_msg += (f"{CRLF_DOT}Unverified Devices > {', '.join(unverified_devices)}") post_event(event_msg) return False @@ -274,7 +273,7 @@ def _list_unverified_devices(retry=False): def stage_5_configure_tracked_devices(): Gb.trace_prefix = 'STAGE 5 > ' - stage_title = 'Stage 5 > Tracked Devices Configuration Summary' + stage_title = f'Stage 5 > Tracked Devices Configuration Summary' try: Gb.EvLog.display_user_message(stage_title) @@ -298,7 +297,7 @@ def stage_5_configure_tracked_devices(): def stage_6_initialization_complete(): Gb.trace_prefix = 'STAGE 6 > ' - stage_title = 'iCloud3 Initialization Complete' + stage_title = f'iCloud3 Initialization Complete' broadcast_info_msg(stage_title) @@ -352,7 +351,7 @@ def stage_7_initial_locate(): Gb.trace_prefix = 'INITLOC > ' post_event("Requesting Initial Locate") - event_msg =(f"{EVLOG_IC3_STARTING}Initializing iCloud3 v{Gb.version} > Complete") + event_msg =(f"{EVLOG_IC3_STARTING}iCloud3 v{Gb.version} > Start up Complete") post_event(event_msg) for Device in Gb.Devices: @@ -397,34 +396,39 @@ def reinitialize_icloud_devices(): alert_msg = f"{EVLOG_ALERT}" if Gb.conf_data_source_ICLOUD: + unverified_devices = [devicename for devicename, Device in Gb.Devices_by_devicename_tracked.items() \ + if Device.verified_flag is False and Device.is_tracked] alert_msg +=(f"One or more devices was not verified. iCloud Location Svcs " - f"may be down, slow to respond or the internet may be down") + f"may be down, slow to respond or the internet may be down." + f"{CRLF_DOT}Unverified Devices > {', '.join(unverified_devices)}") + post_event(alert_msg) - event_msg =(f"{EVLOG_IC3_STARTING}Initializing iCloud3 > Restarting iCloud Location Services") + event_msg =(f"{EVLOG_IC3_STARTING}Restarting iCloud Location Services") post_event(event_msg) if Gb.PyiCloud and Gb.PyiCloud.FamilySharing: Gb.PyiCloud.FamilySharing.refresh_client() - if stage_4_setup_data_sources(): - stage_4_setup_data_sources() + stage_4_success = stage_4_setup_data_sources(retry=None) + if stage_4_success is False: + stage_4_success = stage_4_setup_data_sources() stage_5_configure_tracked_devices() stage_6_initialization_complete() - # stage_7_initial_locate() Gb.all_tracking_paused_flag = False - alert_msg =(f"{EVLOG_ALERT}One or more devices was still not verified" - f"{CRLF}This can be caused by:" - f"{CRLF_DOT}iCloud3 Configuration Errors" - f"{CRLF_DOT}iCloud Location Svcs may be down or slow" - f"{CRLF_DOT}The internet may be down" - f"{CRLF_DOT}The username/password is not set up or incorrect" - f"{CRLF}{'-'*50}{CRLF}Check the Event Log error messages, " - f"correct any problems and restart iCloud3") - post_event(alert_msg) + if stage_4_success is False: + alert_msg =(f"{EVLOG_ALERT}One or more devices was still not verified" + f"{CRLF}This can be caused by:" + f"{CRLF_DOT}iCloud3 Configuration Errors" + f"{CRLF_DOT}iCloud Location Svcs may be down or slow" + f"{CRLF_DOT}The internet may be down" + f"{CRLF_DOT}The username/password is not set up or incorrect" + f"{CRLF}{'-'*50}{CRLF}Check the Event Log error messages, " + f"correct any problems and restart iCloud3") + post_event(alert_msg) return False diff --git a/custom_components/icloud3/support/stationary_zone.py b/custom_components/icloud3/support/stationary_zone.py index c590759..eaae3dc 100644 --- a/custom_components/icloud3/support/stationary_zone.py +++ b/custom_components/icloud3/support/stationary_zone.py @@ -8,7 +8,7 @@ # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> from ..global_variables import GlobalVariables as Gb -from ..const import (ZONE, LATITUDE, LONGITUDE, STATZONE_RADIUS_1M, HIGH_INTEGER, +from ..const import (ZONE, LATITUDE, LONGITUDE, STATZONE_RADIUS_1M, HIGH_INTEGER, ENTER_ZONE, EXIT_ZONE, NEXT_UPDATE, INTERVAL, RARROW, ) from ..zone import iCloud3_StationaryZone from ..support import determine_interval as det_interval @@ -49,16 +49,17 @@ def move_device_into_statzone(Device): _ha_statzones = ha_statzones() # Cycle thru existing ic3 StatZones looking for one that can be recreated at a - # new location. + # new location. for StatZone in Gb.StatZones: if StatZone.passive and StatZone.zone not in _ha_statzones: + event_msg = f"Reusing Stationary Zone > {StatZone.fname_id}" break else: StatZone = create_StationaryZones_object() - event_msg = (f"Created StatZone > {StatZone.fname}, SetupBy-{Device.fname}") - post_event(event_msg) + event_msg = f"Created Stationary Zone > {StatZone.fname_id}, SetupBy-{Device.fname}" + post_event(event_msg) # Set location, it will be updated when Device's attributes are updated in main routine StatZone.latitude = latitude @@ -68,7 +69,7 @@ def move_device_into_statzone(Device): still_since_secs = Device.statzone_timer - Gb.statzone_still_time_secs _clear_statzone_timer_distance(Device, create_statzone_flag=True) - + StatZone.away_attrs[LATITUDE] = latitude StatZone.away_attrs[LONGITUDE] = longitude @@ -81,10 +82,9 @@ def move_device_into_statzone(Device): Device.into_zone_datetime = datetime_now() Device.selected_zone_results = [] - event_msg =(f"Setting Up Stationary Zone ({StatZone.display_as}) > " - f"StationarySince-{format_time_age(still_since_secs)}, " - f"GPS-{format_gps(latitude, longitude, 0)}") - post_event(Device.devicename, event_msg) + # event_msg =(f"Assigned Stationary Zone > {StatZone.display_as}, > " + # f"StationarySince-{format_time_age(still_since_secs)}") + # post_event(Device.devicename, event_msg) iosapp_interface.request_location(Device) _trigger_monitored_device_update(StatZone, Device, ENTER_ZONE) @@ -129,7 +129,7 @@ def exit_all_statzones(): def exit_statzone(Device): ''' Move a device out of this stat zone. Delete the stat zone if there are no - other devces still in it. + other devces still in it. Parameter: Device - The Device exiting the stat zone @@ -138,29 +138,26 @@ def exit_statzone(Device): StatZone = Device.StatZone Device.StatZone = None - removed_msg = "Exited" # If only monitored devices left in the StatZone, take them out of the StatZone # so the zone will be removed if tracked_devices_in_statzone_count(StatZone) == 0: if monitored_devices_in_statzone_count(StatZone) > 0: _trigger_monitored_device_update(StatZone, Device, EXIT_ZONE) - removed_msg += " & Removed" remove_statzone(StatZone, Device) - event_msg =(f"StatZone {removed_msg} ({StatZone.display_as}) > " - f"DevicesInStatZone-{devices_in_statzone_count(StatZone)}") - - post_event(Device.devicename, event_msg) + event_msg =(f"Will Remove Stationary Zone > {StatZone.display_as}, " + f"LastUsedBy-{Device.fname}") + post_event(Device.devicename, event_msg) #-------------------------------------------------------------------- def kill_and_recreate_unuseable_statzone(Device): ''' - There are times when the iOSapp will exit a StatZone when it is still in it. + There are times when the iOSapp will exit a StatZone when it is still in it. It exits and the Devices distance is less than 100m, the iOSapp will not put the device back in it and seems to stop monitoring it. When this happens, create a new StatZone, move any devices in this one to the new one and delete - it. + it. ''' StatZone = Device.StatZone @@ -171,7 +168,7 @@ def kill_and_recreate_unuseable_statzone(Device): _StatZone = move_device_into_statzone(Device) for _Device in Devices_in_statzone: _Device.StatZone = _StatZone - event_msg =(f"Reassign StatZone > {StatZone.display_as}{RARROW}" + event_msg =(f"Reassigned Stationary Zone > {StatZone.display_as}{RARROW}" f"{_StatZone.display_as}") post_event(_Device.devicename, event_msg) @@ -212,12 +209,12 @@ def move_statzone_to_device_location(Device, latitude=None, longitude=None): #-------------------------------------------------------------------- def remove_statzone(StatZone, Device=None): - ''' - Delete the stationary zone. It will be removed from HA but the StatZone + ''' + Delete the stationary zone. It will be removed from HA but the StatZone object still exists in iCloud3 and will be recreated when a new one is needed. It is set to passive so it will not be selected. It's remove - time is set so it will not reuse the same StatZone in the next 5-minutes. - This gives the iOSapp time to remove it from its monitored zones. + time is set so it will not reuse the same StatZone in the next 5-minutes. + This gives the iOSapp time to remove it from its monitored zones. ''' if StatZone is None: return @@ -225,19 +222,22 @@ def remove_statzone(StatZone, Device=None): StatZone.radius_m = STATZONE_RADIUS_1M StatZone.passive = True - StatZone.remove_ha_zone() + #StatZone.remove_ha_zone() + if StatZone not in Gb.StatZones_to_delete: + Gb.StatZones_to_delete.append(StatZone) if Device: _clear_statzone_timer_distance(Device) - event_msg = f"Exited Stationary Zone ({StatZone.display_as})" + event_msg =(f"Exited Stationary Zone > {StatZone.display_as}, " + f"DevicesRemaining-{devices_in_statzone_count(StatZone)}") post_event(Device.devicename, event_msg) #-------------------------------------------------------------------- def ha_statzones(): - ''' + ''' Return a list of StatZones in the yaml zone collection updated when iCloud3 Add our remove a StatZone - ''' + ''' Gb.hass.services.call(ZONE, "reload") return [zone.replace('zone.', '') for zone in Gb.hass.data['zone_entity_ids'] if zone.startswith('zone.ic3_stationary_')] @@ -247,16 +247,15 @@ def _trigger_monitored_device_update(StatZone, Device, action): for _Device in Gb.Devices_by_devicename_monitored.values(): if action == ENTER_ZONE: dist_m = _Device.distance_m(Device.loc_data_latitude, Device.loc_data_longitude) - event_msg = f"Trigger > StatZone Created ({StatZone.display_as}) {dist_m=}" + event_msg = f"Trigger > Create Stationary Zone > {StatZone.display_as}" post_event(_Device.devicename, event_msg) elif action == EXIT_ZONE and _Device.StatZone is StatZone: - # _Device.StatZone = None - event_msg = f"Trigger > StatZone Removed ({StatZone.display_as})" + event_msg = f"Trigger > Remove Stationary Zone > {StatZone.display_as}" post_event(_Device.devicename, event_msg) else: - continue + continue Gb.force_icloud_update_flag = True det_interval.update_all_device_fm_zone_sensors_interval(_Device, 5) diff --git a/custom_components/icloud3/support/waze.py b/custom_components/icloud3/support/waze.py index 257c949..7244be4 100644 --- a/custom_components/icloud3/support/waze.py +++ b/custom_components/icloud3/support/waze.py @@ -272,7 +272,7 @@ def get_history_time_distance(self, Device, FromZone, check_hist_db=True): and route_time > 0 and route_dist_km > 0): Gb.WazeHist.update_usage_cnt(location_id) - waze_source_msg = f"Using Route History Database, Recd-{location_id} " + waze_source_msg = f"Using Route History Database (#{location_id})" else: # Zone's location changed in WazeHist or invalid data. Get from Waze later diff --git a/custom_components/icloud3/zone.py b/custom_components/icloud3/zone.py index 0d56e40..7a2cb47 100644 --- a/custom_components/icloud3/zone.py +++ b/custom_components/icloud3/zone.py @@ -188,8 +188,7 @@ def __init__(self, statzone_id): self.base_attrs = {} self.fname = f"StatZon{self.statzone_id}" - self.display_as = self.fname - Gb.zone_display_as[self.zone] = self.fname + self.fname_id = self.display_as = Gb.zone_display_as[self.zone] = self.fname #base_attrs is used to move the stationary zone back to it's base self.base_attrs[NAME] = self.zone @@ -230,7 +229,9 @@ def __init__(self, statzone_id): def initialize_updatable_items(self): if Gb.statzone_fname == '': Gb.statzone_fname = 'StatZon#' self.fname = Gb.statzone_fname.replace('#', self.statzone_id) - Gb.zone_display_as[self.zone] = self.fname + self.fname_id = self.display_as = Gb.zone_display_as[self.zone] = self.fname + if instr(Gb.statzone_fname, '#') is False: + self.fname_id = f"{self.fname} (..._{self.statzone_id})" self.base_latitude = 0 #Gb.statzone_base_latitude self.base_longitude = 0 #Gb.statzone_base_longitude @@ -288,9 +289,8 @@ def remove_ha_zone(self): try: Gb.hass.states.async_remove(f"zone.{self.zone}") - # item = Gb.StatZones_by_zone.pop(self.zone, None) - post_monitor_msg(f"REMOVED StationaryZone > {self.fname} ({self.zone})") + post_event(f"Removed HA Zone > {self.fname_id}") except Exception as err: pass