diff --git a/custom_components/icloud3/ChangeLog.txt b/custom_components/icloud3/ChangeLog.txt index f102f85..9516784 100644 --- a/custom_components/icloud3/ChangeLog.txt +++ b/custom_components/icloud3/ChangeLog.txt @@ -1,3 +1,14 @@ +Beta 19.1 - 7/10/2023 +................... +1. Bug Fix - Fixed a problem that may be encountered if you have a watch paired to an iPhone and are tracking the iPhone but not the watch. +2. New Sensors: + 1. travel_time_hhmm - Displays the travel time to the zone in an hh:mm format. + 2. arrival_time - The expected arrival time to the zone (current time + travel time) +3. Simplified the sensors associated with track_from_zones other than Home. + + + + Beta 19 - 7/8/2023 ................... 1. Event Log - You will get a browser refresh notification diff --git a/custom_components/icloud3/__init__.py b/custom_components/icloud3/__init__.py index b370531..25d0a90 100644 --- a/custom_components/icloud3/__init__.py +++ b/custom_components/icloud3/__init__.py @@ -6,28 +6,27 @@ from homeassistant.config_entries import ConfigEntry -from homeassistant.core import CoreState, HomeAssistant +from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP +from homeassistant.core import HomeAssistant from homeassistant.helpers.typing import ConfigType from homeassistant.helpers import network -from homeassistant.const import EVENT_HOMEASSISTANT_STARTED, EVENT_HOMEASSISTANT_STOP from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.components.network import async_get_source_ip -# from homeassistant.helpers import collection, storage -import homeassistant.util.location as ha_location_info import homeassistant.helpers.config_validation as cv import homeassistant.util.dt as dt_util -# import voluptuous as vol +import homeassistant.util.location as ha_location_info import os +import logging from .const import (DOMAIN, PLATFORMS, MODE_PLATFORM, MODE_INTEGRATION, CONF_VERSION, CONF_SETUP_ICLOUD_SESSION_EARLY, SENSOR_EVENT_LOG_NAME, SENSOR_WAZEHIST_TRACK_NAME, EVLOG_IC3_STARTING, VERSION, ) -from .const_sensor import (HA_EXCLUDE_SENSORS, ) +CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN) + +# from .const_sensor import (HA_EXCLUDE_SENSORS, ) from .global_variables import GlobalVariables as Gb -# from .helpers.common import (instr, ) from .helpers.messaging import (_trace, _traceha, open_ic3_log_file, log_info_msg, log_debug_msg, log_error_msg, log_exception) from .support.v2v3_config_migration import iCloud3_v2v3ConfigMigration @@ -41,7 +40,6 @@ from .icloud3_main import iCloud3 from . import config_flow -import logging # _LOGGER = logging.getLogger(__name__) Gb.HALogger = _LOGGER = logging.getLogger('icloud3') @@ -63,8 +61,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: Gb.config = config Gb.ha_config_platform_stmt = True Gb.operating_mode = MODE_PLATFORM - Gb.local_ip = await async_get_source_ip(hass) - Gb.network_url = network.get_url(hass) await async_get_ha_location_info(hass) recorder_prefilter.add_filter(hass, [SENSOR_EVENT_LOG_NAME, SENSOR_WAZEHIST_TRACK_NAME]) @@ -125,8 +121,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): Gb.config_entry = entry Gb.entry_id = entry.entry_id Gb.operating_mode = MODE_INTEGRATION - Gb.local_ip = await async_get_source_ip(hass) - Gb.network_url = network.get_url(hass) await async_get_ha_location_info(hass) recorder_prefilter.add_filter(hass, [SENSOR_EVENT_LOG_NAME, SENSOR_WAZEHIST_TRACK_NAME]) diff --git a/custom_components/icloud3/config_flow.py b/custom_components/icloud3/config_flow.py index 3704252..f2167ed 100644 --- a/custom_components/icloud3/config_flow.py +++ b/custom_components/icloud3/config_flow.py @@ -340,6 +340,8 @@ def dict_value_to_list(key_value_dict): 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', } CONF_SENSORS_TRACKING_DISTANCE_KEY_TEXT = { 'home_distance': 'home_distance > Distance to the Home zone', @@ -348,12 +350,11 @@ def dict_value_to_list(key_value_dict): 'moved_distance': 'moved_distance > Distance moved from the last location', } CONF_SENSORS_TRACK_FROM_ZONES_KEY_TEXT = { - 'tfz_zone_info': 'zone_info_[zone] > Summary sensor with all zone distance & time attributes', - 'tfz_travel_time': 'travel_time_[zone] > Waze Travel time to a Track-from-Zone', - 'tfz_travel_time_min': 'travel_time_min_[zone] > Waze Travel time to a Track-from-Zone in minutes', - 'tfz_distance': 'distance_[zone] > Distance from the Track-from-Zone ', - 'tfz_dir_of_travel':'dir_of_travel_[zone] > Direction of Travel from the Track-from-Zone (Towards, AwayFrom, inZone, etc)', + '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', @@ -405,7 +406,7 @@ def dict_value_to_list(key_value_dict): "App issues an Enter Zone trigger when the device enters the zone and changes the " "device_tracker entity state to the Zone. iCloud3 does not process the Enter Zone " "trigger until the delay time has passed. This prevents processing a Zone Enter " - "trigger that is immediately followed by an Exit Zone trigger.") + "trig[er that is immediately followed by an Exit Zone trigger.") STAT_ZONE_HEADER = ("A Stationary Zone is automatically created if the device remains in the same location " "(store, friends house, doctor`s office, etc.) for an extended period of time") STAT_ZONE_BASE_HEADER = ("The Stationary Zone is moved to it's 'Base Location' when it is not used by the device. " @@ -1201,6 +1202,15 @@ 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_base = ['zone_info'] + tfz_sensors_base.extend(user_input[CONF_SENSORS_TRACKING_TIME]) + tfz_sensors_base.extend(user_input[CONF_SENSORS_TRACKING_DISTANCE]) + tfz_sensors = [] + for sensor in tfz_sensors_base: + if sensor in SENSOR_GROUPS['track_from_zone']: + tfz_sensors.append(f"tfz_{sensor}") + user_input[CONF_SENSORS_TRACK_FROM_ZONES] = tfz_sensors + if action_item == 'exclude_sensors': self.excluded_sensors = Gb.conf_sensors[CONF_EXCLUDED_SENSORS].copy() self.sensors_list_filter = '?' @@ -2939,7 +2949,6 @@ def _any_errors(self): def _remove_and_create_sensors(self, user_input): """ Remove unchecked sensor entities and create newly checked sensor entities """ - new_sensors_list, remove_sensors_list = \ self._sensor_form_identify_new_and_removed_sensors(user_input) self._remove_sensor_entity(remove_sensors_list) @@ -4277,9 +4286,9 @@ def form_schema(self, step_id): vol.Required(CONF_SENSORS_OTHER, default=Gb.conf_sensors[CONF_SENSORS_OTHER]): cv.multi_select(CONF_SENSORS_OTHER_KEY_TEXT), - vol.Required(CONF_SENSORS_TRACK_FROM_ZONES, - default=Gb.conf_sensors[CONF_SENSORS_TRACK_FROM_ZONES]): - cv.multi_select(CONF_SENSORS_TRACK_FROM_ZONES_KEY_TEXT), + # vol.Required(CONF_SENSORS_TRACK_FROM_ZONES, + # default=Gb.conf_sensors[CONF_SENSORS_TRACK_FROM_ZONES]): + # cv.multi_select(CONF_SENSORS_TRACK_FROM_ZONES_KEY_TEXT), vol.Required(CONF_SENSORS_MONITORED_DEVICES, default=Gb.conf_sensors[CONF_SENSORS_MONITORED_DEVICES]): cv.multi_select(CONF_SENSORS_MONITORED_DEVICES_KEY_TEXT), diff --git a/custom_components/icloud3/const.py b/custom_components/icloud3/const.py index 9fed19a..76f40aa 100644 --- a/custom_components/icloud3/const.py +++ b/custom_components/icloud3/const.py @@ -4,7 +4,7 @@ # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -VERSION = '3.0.0b19.0' +VERSION = '3.0.0b19.1' DOMAIN = 'icloud3' ICLOUD3 = 'iCloud3' @@ -126,6 +126,7 @@ DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' DATETIME_ZERO = '0000-00-00 00:00:00' HHMMSS_ZERO = '00:00:00' +HHMM_ZERO = '00:00' HIGH_INTEGER = 9999999999 # Device Tracking Status @@ -635,6 +636,9 @@ CONF_SENSORS_TRACKING_TIME = 'tracking_time' TRAVEL_TIME = "travel_time" TRAVEL_TIME_MIN = "travel_time_min" +TRAVEL_TIME_HHMM = "travel_time_hhmm" +ARRIVAL_TIME = "arrival_time" + CONF_SENSORS_TRACKING_DISTANCE = 'tracking_distance' ZONE_DISTANCE_M = 'meters_distance' @@ -653,6 +657,8 @@ TFZ_ZONE_DISTANCE = 'tfz_zone_distance' TFZ_TRAVEL_TIME = 'tfz_travel_time' TFZ_TRAVEL_TIME_MIN = 'tfz_travel_time_min' +TFZ_TRAVEL_TIME_HHMM = "tfz_travel_time_hhmm" +TFZ_ARRIVAL_TIME = "tfz_arrival_time" TFZ_DIR_OF_TRAVEL = 'tfz_dir_of_travel' CONF_SENSORS_TRACKING_OTHER = 'tracking_other' @@ -851,14 +857,20 @@ NEXT_UPDATE, ], CONF_SENSORS_TRACKING_TIME: [ TRAVEL_TIME, - TRAVEL_TIME_MIN, ], + TRAVEL_TIME_MIN, + ARRIVAL_TIME, ], CONF_SENSORS_TRACKING_DISTANCE: [ HOME_DISTANCE, ZONE_DISTANCE, MOVED_DISTANCE, DIR_OF_TRAVEL, ], CONF_SENSORS_TRACK_FROM_ZONES: [ - TFZ_ZONE_INFO, ], + TFZ_ZONE_INFO, + TFZ_TRAVEL_TIME, + TFZ_TRAVEL_TIME_MIN, + TFZ_ARRIVAL_TIME, + TFZ_ZONE_DISTANCE, + TFZ_DIR_OF_TRAVEL,], CONF_SENSORS_TRACKING_OTHER: [], CONF_SENSORS_ZONE: [ ZONE_NAME], @@ -1023,7 +1035,7 @@ TIMESTAMP, TIMESTAMP_SECS, TIMESTAMP_TIME, LOCATION_TIME, DATETIME, AGE, TRIGGER, BATTERY, BATTERY_LEVEL, BATTERY_STATUS, INTERVAL, ZONE_DISTANCE, HOME_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, MOVED_DISTANCE, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, MOVED_DISTANCE, DEVICE_STATUS, LOW_POWER_MODE, TRACKING, DEVICENAME_IOSAPP, AUTHENTICATED, diff --git a/custom_components/icloud3/const_sensor.py b/custom_components/icloud3/const_sensor.py index 9a11a04..ebca3d2 100644 --- a/custom_components/icloud3/const_sensor.py +++ b/custom_components/icloud3/const_sensor.py @@ -14,15 +14,15 @@ BATTERY_SOURCE, BATTERY, BATTERY_STATUS, BATTERY_UPDATE_TIME, DISTANCE, ZONE_DISTANCE, ZONE_DISTANCE_M, ZONE_DISTANCE_M_EDGE, HOME_DISTANCE, MAX_DISTANCE,CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, MOVED_DISTANCE, MOVED_TIME_FROM, MOVED_TIME_TO, DEVICE_STATUS, LAST_UPDATE, LAST_UPDATE_DATETIME, NEXT_UPDATE, NEXT_UPDATE_DATETIME, LAST_LOCATED, LAST_LOCATED_DATETIME, INFO, GPS_ACCURACY, ALTITUDE, VERTICAL_ACCURACY, - TFZ_ZONE_INFO, TFZ_DISTANCE, TFZ_ZONE_DISTANCE, TFZ_TRAVEL_TIME, - TFZ_TRAVEL_TIME_MIN, TFZ_DIR_OF_TRAVEL, + TFZ_ZONE_INFO, TFZ_DISTANCE, TFZ_ZONE_DISTANCE, TFZ_DIR_OF_TRAVEL, + TFZ_TRAVEL_TIME,TFZ_TRAVEL_TIME_MIN, TFZ_TRAVEL_TIME_HHMM, TFZ_ARRIVAL_TIME, TOWARDS, AWAY_FROM, TOWARDS_HOME, AWAY_FROM_HOME, INZONE, INZONE_HOME, INZONE_STATIONARY, SENSOR_EVENT_LOG_NAME, SENSOR_WAZEHIST_TRACK_NAME, ) @@ -38,18 +38,19 @@ GPS_ACCURACY, ALTITUDE, VERTICAL_ACCURACY, ] SENSOR_LIST_TRACKING = [NEXT_UPDATE, LAST_UPDATE, LAST_LOCATED, - TRAVEL_TIME, TRAVEL_TIME_MIN, MOVED_DISTANCE, DIR_OF_TRAVEL, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, + MOVED_DISTANCE, DIR_OF_TRAVEL, WAZE_DISTANCE, CALC_DISTANCE, ZONE_DISTANCE, ZONE_DISTANCE_M, ZONE_DISTANCE_M_EDGE, HOME_DISTANCE, ZONE, ZONE_FNAME, ZONE_NAME, ZONE_DATETIME, LAST_ZONE, LAST_ZONE_FNAME, LAST_ZONE_NAME, ] SENSOR_LIST_TRACK_FROM_ZONE = [INFO, LAST_UPDATE, NEXT_UPDATE, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, ] SENSOR_LIST_LOC_UPDATE =[TRIGGER, INTERVAL, NEXT_UPDATE, LAST_UPDATE, LAST_LOCATED, - TRAVEL_TIME, TRAVEL_TIME_MIN, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, ] SENSOR_LIST_ZONE_NAME =[ZONE, ZONE_DISPLAY_AS, ZONE_FNAME, ZONE_NAME, ZONE_NAME, ZONE_FNAME, LAST_ZONE_NAME, LAST_ZONE_DISPLAY_AS, LAST_ZONE_FNAME, LAST_ZONE, @@ -65,10 +66,12 @@ NAME, ZONE, ZONE_FNAME, ZONE_NAME, ZONE_DATETIME, HOME_DISTANCE, - TRAVEL_TIME, TRAVEL_TIME_MIN, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, LAST_LOCATED, - LAST_UPDATE, - ], + LAST_UPDATE,], + 'track_from_zone': [ + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, + ZONE_DISTANCE, DISTANCE, DIR_OF_TRAVEL, 'zone_info', ] } SENSOR_ICONS = { TOWARDS_HOME: 'mdi:home-import-outline', @@ -200,14 +203,26 @@ 'TravelTime', 'timer, min', 'mdi:clock-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], 0], TRAVEL_TIME_MIN: [ 'TravelTimeMin', 'timer', 'mdi:clock-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], 0], + TRAVEL_TIME_HHMM: [ + 'TravelTime (hh:mm)', + 'text', + 'mdi:clock-outline', + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], + BLANK_SENSOR_FIELD], + ARRIVAL_TIME: [ + 'ArrivalTime', + 'text', + 'mdi:clock-outline', + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], + BLANK_SENSOR_FIELD], # CONF_SENSORS_TRACKING_DISTANCE ZONE_DISTANCE: [ @@ -235,7 +250,7 @@ 'Direction', 'text, title', 'mdi:compass-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], BLANK_SENSOR_FIELD], MOVED_DISTANCE: [ 'MovedDistance', @@ -247,7 +262,7 @@ 'ZoneInfo', 'zone_info', 'mdi:map-marker-radius-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DISTANCE, MAX_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, DIR_OF_TRAVEL], BLANK_SENSOR_FIELD], @@ -256,20 +271,32 @@ 'ZoneInfo', 'zone_info', 'mdi:map-marker-radius-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DISTANCE, MAX_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, DIR_OF_TRAVEL], BLANK_SENSOR_FIELD], TFZ_TRAVEL_TIME: [ 'TravelTime', 'timer, mins', 'mdi:clock-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], BLANK_SENSOR_FIELD], TFZ_TRAVEL_TIME_MIN: [ 'TravelTimeMin', 'timer', 'mdi:clock-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], + BLANK_SENSOR_FIELD], + TFZ_TRAVEL_TIME_HHMM: [ + 'TravelTime (hh:mm)', + 'text', + 'mdi:clock-outline', + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], + BLANK_SENSOR_FIELD], + TFZ_ARRIVAL_TIME: [ + 'ArrivalTime', + 'text', + 'mdi:clock-outline', + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME], BLANK_SENSOR_FIELD], TFZ_DISTANCE: [ 'ZoneDistance', @@ -289,7 +316,8 @@ 'Direction', 'text, title', 'mdi:compass-outline', - [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, DISTANCE, MAX_DISTANCE], + [FROM_ZONE, TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, + DISTANCE, MAX_DISTANCE], BLANK_SENSOR_FIELD], # CONF_SENSORS_TRACKING_OTHER diff --git a/custom_components/icloud3/device.py b/custom_components/icloud3/device.py index cf1a985..b2d9b45 100644 --- a/custom_components/icloud3/device.py +++ b/custom_components/icloud3/device.py @@ -11,7 +11,7 @@ TOWARDS, AWAY, AWAY_FROM, INZONE, STATIONARY, STATIONARY_FNAME, TOWARDS_HOME, AWAY_FROM_HOME, INZONE_HOME, INZONE_STATIONARY, PAUSED, PAUSED_CAPS, RESUMING, - DATETIME_ZERO, HHMMSS_ZERO, HIGH_INTEGER, + DATETIME_ZERO, HHMMSS_ZERO,HHMM_ZERO, HIGH_INTEGER, TRACKING_NORMAL, TRACKING_PAUSED, TRACKING_RESUMED, LAST_CHANGED_SECS, LAST_CHANGED_TIME, STATE, EVLOG_ALERT, @@ -32,7 +32,7 @@ BATTERY_SOURCE, BATTERY, BATTERY_LEVEL, BATTERY_STATUS, BATTERY_STATUS_FNAME, BATTERY_UPDATE_TIME, ZONE_DISTANCE, ZONE_DISTANCE_M, ZONE_DISTANCE_M_EDGE, HOME_DISTANCE, MAX_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, MOVED_DISTANCE, MOVED_TIME_FROM, MOVED_TIME_TO, DEVICE_STATUS, LOW_POWER_MODE, RAW_MODEL, MODEL, MODEL_DISPLAY_NAME, LAST_UPDATE, LAST_UPDATE_TIME, LAST_UPDATE_DATETIME, @@ -368,6 +368,8 @@ def initialize_sensors(self): self.sensors[LAST_UPDATE] = HHMMSS_ZERO self.sensors[TRAVEL_TIME] = 0 self.sensors[TRAVEL_TIME_MIN] = 0 + self.sensors[TRAVEL_TIME_HHMM] = HHMM_ZERO + self.sensors[ARRIVAL_TIME] = HHMMSS_ZERO self.sensors[ZONE_DISTANCE] = 0 self.sensors[ZONE_DISTANCE_M] = 0 self.sensors[ZONE_DISTANCE_M_EDGE] = 0 @@ -1830,8 +1832,10 @@ def update_sensor_values_from_data_fields(self): self.sensors[LAST_UPDATE_DATETIME] = self.DeviceFmZoneClosest.sensors[LAST_UPDATE_DATETIME] self.sensors[LAST_UPDATE_TIME] = self.DeviceFmZoneClosest.sensors[LAST_UPDATE_TIME] self.sensors[LAST_UPDATE] = self.DeviceFmZoneClosest.sensors[LAST_UPDATE] - self.sensors[TRAVEL_TIME_MIN] = self.DeviceFmZoneClosest.sensors[TRAVEL_TIME_MIN] self.sensors[TRAVEL_TIME] = self.DeviceFmZoneClosest.sensors[TRAVEL_TIME] + self.sensors[TRAVEL_TIME_MIN] = self.DeviceFmZoneClosest.sensors[TRAVEL_TIME_MIN] + self.sensors[TRAVEL_TIME_HHMM] = self.DeviceFmZoneClosest.sensors[TRAVEL_TIME_HHMM] + self.sensors[ARRIVAL_TIME] = self.DeviceFmZoneClosest.sensors[ARRIVAL_TIME] self.sensors[ZONE_DISTANCE] = self.DeviceFmZoneClosest.sensors[ZONE_DISTANCE] self.sensors[ZONE_DISTANCE_M] = self.DeviceFmZoneClosest.sensors[ZONE_DISTANCE_M] self.sensors[ZONE_DISTANCE_M_EDGE] = self.DeviceFmZoneClosest.sensors[ZONE_DISTANCE_M_EDGE] diff --git a/custom_components/icloud3/device_fm_zone.py b/custom_components/icloud3/device_fm_zone.py index ea80892..fce9870 100644 --- a/custom_components/icloud3/device_fm_zone.py +++ b/custom_components/icloud3/device_fm_zone.py @@ -18,13 +18,13 @@ from .global_variables import GlobalVariables as Gb from .const import (HOME, NOT_SET, - DATETIME_ZERO, HHMMSS_ZERO, + DATETIME_ZERO, HHMMSS_ZERO, HHMM_ZERO, TOWARDS, AWAY_FROM, INTERVAL, DISTANCE, ZONE_DISTANCE, ZONE_DISTANCE_M, ZONE_DISTANCE_M_EDGE, MAX_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, FROM_ZONE, ZONE_INFO, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, MOVED_DISTANCE, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, MOVED_DISTANCE, LAST_LOCATED, LAST_LOCATED_TIME, LAST_LOCATED_DATETIME, LAST_UPDATE, LAST_UPDATE_TIME, LAST_UPDATE_DATETIME, NEXT_UPDATE, NEXT_UPDATE_TIME, NEXT_UPDATE_DATETIME, @@ -106,6 +106,8 @@ def initialize_sensors(self): self.sensors[LAST_UPDATE] = HHMMSS_ZERO self.sensors[TRAVEL_TIME] = 0 self.sensors[TRAVEL_TIME_MIN] = 0 + self.sensors[TRAVEL_TIME_HHMM] = HHMM_ZERO + self.sensors[ARRIVAL_TIME] = HHMMSS_ZERO self.sensors[DISTANCE] = 0 self.sensors[MAX_DISTANCE] = 0 self.sensors[ZONE_DISTANCE] = 0 diff --git a/custom_components/icloud3/event_log_card/icloud3-event-log-card.js b/custom_components/icloud3/event_log_card/icloud3-event-log-card.js index c35011a..09846ae 100644 --- a/custom_components/icloud3/event_log_card/icloud3-event-log-card.js +++ b/custom_components/icloud3/event_log_card/icloud3-event-log-card.js @@ -366,14 +366,14 @@ class iCloud3EventLogCard extends HTMLElement { btnConfig.classList.add("btnConfig") btnConfig.setAttribute('href', 'http://10.0.2.201:8123/config/integrations/integration/icloud3') btnConfig.setAttribute('target', '_self') - btnConfig.innerHTML = ` + btnConfig.innerHTML = ` - - ` + + ` const btnHeart = document.createElement('A') btnHeart.id = "btnHeart" diff --git a/custom_components/icloud3/helpers/time_util.py b/custom_components/icloud3/helpers/time_util.py index 6dcf37d..e6f36d1 100644 --- a/custom_components/icloud3/helpers/time_util.py +++ b/custom_components/icloud3/helpers/time_util.py @@ -121,30 +121,50 @@ def secs_to_hhmmss(secs): return '00:00:00' return f"{hh}:{mm}:{ss}" -#-------------------------------------------------------------------- -def secs_to_dhms_str(secs): - """ Create the time 0w0d0h0m0s time string from seconds """ - return f"{secs/86400:.2f} days" +#--------------------------------------------------------- +def secs_to_hhmm(secs): + """ secs --> hh:mm """ + + try: + if instr(secs, ':'): + return secs + + w_secs = float(secs) + 30 + + hh = f"{int(w_secs // 3600):02}" if (w_secs >= 3600) else '00' + w_secs = w_secs % 3600 + mm = f"{int(w_secs // 60):02}" if (w_secs >= 60) else '00' + + return f"{hh}:{mm}" + + except: + return '00:00' + +#-------------------------------------------------------------------- +def secs_to_time_hhmm(secs): + """ secs --> hh:mm or hh:mma or hh:mmp""" + try: + hhmmss = secs_to_time(secs + 30) + hhmm = hhmmss[:-3] + if (Gb.time_format_12_hour is False + or hhmm.endswith('a') + or hhmm.endswith('p')): + return hhmm - #try: - # secs_dhms = float(secs) - # dhms_str = "" - # if (secs >= 31557600): - # return f"{round(secs_dhms/31557600, 2)}y " + if hhmm.endswith(':'): hhmm = hhmm[:-1] + hhmm = hhmm + hhmmss[-1:] - # if (secs >= 604800): dhms_str += f"{secs_dhms // 604800}w " - # secs_dhms = secs_dhms % 604800 - # if (secs >= 86400): dhms_str += f"{secs_dhms // 86400}d " - # secs_dhms = secs_dhms % 86400 - # if (secs >= 3600): dhms_str += f"{secs_dhms / 3600:.1f}h" + return hhmm - # dhms_str = dhms_str.replace('.0', '') + except: + return '00:00' - #except: - # dhms_str = "" +#-------------------------------------------------------------------- +def secs_to_dhms_str(secs): + """ Create the time 0w0d0h0m0s time string from seconds """ - #return dhms_str + return f"{secs/86400:.2f} days" #-------------------------------------------------------------------- def waze_mins_to_time_str(waze_time_from_zone): @@ -196,6 +216,14 @@ def time_to_secs(hhmmss): return secs +#-------------------------------------------------------------------- +def secs_to_time(secs): + """ Convert seconds to hh:mm:ss """ + if secs is None or secs == 0 or secs == HIGH_INTEGER: + return HHMMSS_ZERO + + return time_to_12hrtime(secs_to_24hr_time(secs)) + #-------------------------------------------------------------------- def secs_to_24hr_time(secs): """ Convert seconds to hh:mm:ss """ @@ -209,14 +237,6 @@ def secs_to_24hr_time(secs): return hhmmss -#-------------------------------------------------------------------- -def secs_to_time(secs): - """ Convert seconds to hh:mm:ss """ - if secs is None or secs == 0 or secs == HIGH_INTEGER: - return HHMMSS_ZERO - - return time_to_12hrtime(secs_to_24hr_time(secs)) - #-------------------------------------------------------------------- def time_to_12hrtime(hhmmss, ampm=True): ''' diff --git a/custom_components/icloud3/icloud3_main.py b/custom_components/icloud3/icloud3_main.py index 9d5a57e..ad99e3c 100644 --- a/custom_components/icloud3/icloud3_main.py +++ b/custom_components/icloud3/icloud3_main.py @@ -273,7 +273,7 @@ def _polling_loop_5_sec_device(self, ha_timer_secs): self._main_5sec_loop_update_battery_iosapp(Device) if self.loop_ctrl_device_update_in_process: - self._display_loop_control_msg('Moitored') + self._display_loop_control_msg('Monitored') break if Device.is_tracking_paused: continue diff --git a/custom_components/icloud3/strings.json b/custom_components/icloud3/strings.json index f61defb..5eeaa5d 100644 --- a/custom_components/icloud3/strings.json +++ b/custom_components/icloud3/strings.json @@ -2,13 +2,12 @@ "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. If 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. If you are deleting and then reinstalling, restart HA first and then reinstall iCloud3", "login_error": "An error occurred logging into the iCloud 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" + "icloud3_init_error": "A problem was encountered initializing the iCloud3 Configuration Wizard. iCloud3 has probably encountered an error during initialization and was not started. Check the 'home-assistant.log' file for any errors related to iCloud3" }, "error": { "invalid_path": "The path provided is not valid. Should be in the format `user/repo-name`", @@ -27,7 +26,7 @@ "title": "iCloud3 Integration Installation" }, "reauth": { - "title": "Apple ID Verification Code (HA Notifications)", + "title": "Apple ID Verification Code", "description": "Enter the 6-digit verification code you just received from Apple", "data": { "verification_code": "ICLOUD ACCOUNT VERIFICATION CODE", @@ -38,9 +37,9 @@ }, "options": { "abort": { + "no_device": "None of your devices have \"Find my iPhone\" activated", "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", "reauth_successful": "The reauthentication has been successfully completed" }, "error": { @@ -51,9 +50,8 @@ "icloud_acct_logging_into": "Logging into iCloud Account", "icloud_acct_logged_into": "Successfully Logged into the iCloud Account", "icloud_acct_already_logged_into": "Already Logged into the iCloud Account", - "icloud_acct_login_error": "iCloud Account Loginu Unsuccessful, Invalid Username or Password", + "icloud_acct_login_error": "iCloud Account Login Failed, Check Username & Password", "icloud_acct_not_set_up": "The iCloud Account Username or Password has not been set up", - "icloud_acct_no_data_source": "No Data Source has been selected", "verification_code_requested": "The Apple ID Verification Code was requested, BROWSER REFRESH MAY BE NEEDED", "verification_code_requested2": "The Apple ID Verification Code was requested", @@ -115,7 +113,7 @@ }, "step": { "menu": { - "title": "iCloud3 Configure Settings", + "title": "iCloud3 Configuration Wizard", "data": { "menu_items": "", "action_items": "═════════════════════════════════════════════════════" @@ -129,13 +127,14 @@ } }, "icloud_account": { - "title": "Data Sources - iCloud Account & iOS App", - "description": "", + "title": "iCloud Account Login Credentials", + "description": "The iCloud Account and the iOS App are used to provide location information (the data source) for tracking a device. This screen is used to configure the username/password that provides access to your iCloud account and to specify if the iOS App will be used on any of your devices. The iCloud Account can provide location information for devices in the Family Sharing list and from those that are sharing location information on the FindMy app. The HA iOS Companion app can also provide location information for the device.", "data": { - "data_source_icloud": "ICLOUD - Location data is provided by the Apple iCloud account", - "username": "APPPLE ID - The email address used to sign in to the iCloud Account", - "password": "PASSWORD - The iCloud Account Password", - "data_source_iosapp": "iOS APP - Location data is provided by the HA iOS App", + "username": "APPPLE ID - The email address used to sign in to the iCloud Acount", + "password": "PASSWORD - The Password of the iCloud Acount", + "data_source1": "LOCATION DATA SOURCE - The services used for location and other data (iCloud, iOS App, both)", + "data_source2": "LOCATION DATA SOURCE - The services providing location and other data", + "data_source": "", "icloud_server_endpoint_suffix": "ICLOUD SERVER LOCATION - Countries having localized Apple iCloud Servers (Not Normally Used)s", "action_items": "═════════════════════════════════════════════════════" }, @@ -165,7 +164,7 @@ "fname": "FRIENDLY NAME - Displayed in HA device_tracker and sensor names and on the Event Log", "device_type": "DEVICE TYPE - iPhone, iPad, Watch, etc.", "tracking_mode": "TRACKING MODE - How location requests should be done (Full tracking, Monitor, Inactive)", - "iosapp": "iOS APP INSTALLED - The HA iOS App is installed on this device ", + "iosapp": "iOS APP MONITORED - Is the iOS App is installed on this device and will it monitored", "action_items": "═════════════════════════════════════════════════════" }, "data_description": { @@ -223,8 +222,6 @@ "device_tracker_state_format": "DEVICE_TRACKER STATE FORMAT - How the Zone name is displayed in the device_tracker entity", "time_format": "TIME FORMAT - How time fields are displayed in sensors and in the Event Log", "unit_of_measurement": "UNIT OF MEASUREMENT - How distance fieldsare displayed in sensors and in the Event Log", - "display_gps_lat_long": "DISPLAY GPS COORDINATES - Display the GPS (Latitude, Longitude/±Accuracy) or only the GPS (/±Accuracy) in the Event Log", - "display_gps_lat_long2": "DISPLAY GPS COORDINATES - Display the GPS Latitude/Longitude/Accuracy (22.32771, -76.33073/±35m) or only the GPS accuracy (/±35m) in the Event Log", "action_items": "═════════════════════════════════════════════════════" }, "data_description": { @@ -270,7 +267,6 @@ "offline_interval": "DEVICE OFFLINE INTERVAL", "travel_time_factor": "TRAVEL TIME INTERVAL MULTIPLIER", "event_log_card_directory": "EVENT LOG CARD LOVELACE RESOURCES DIRECTORY - Event Log custom card .js file directory", - "ha_config_ic3_url": "HA INTEGRATIONS > ICLOUD3 CONFIGURE URL - Special URL used to display the Configure Settings screens from the EvLog Icon", "action_items": "═════════════════════════════════════════════════════" }, "data_description": { @@ -283,8 +279,7 @@ "exit_zone_interval": "The time to the first location request after exiting a zone", "iosapp_alive_interval": "Send a location request to the iOS App if there has been no contact after this amount of time. This will check to see if the iOS 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": "Next location time = Waze travel time to zone * this value", - "ha_config_ic3_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." + "travel_time_factor": "Next location time = Waze travel time to zone * this value" } }, "inzone_intervals": { diff --git a/custom_components/icloud3/support/determine_interval.py b/custom_components/icloud3/support/determine_interval.py index 5ff9068..af3022e 100644 --- a/custom_components/icloud3/support/determine_interval.py +++ b/custom_components/icloud3/support/determine_interval.py @@ -35,7 +35,7 @@ ZONE, ZONE_INFO, INTERVAL, DISTANCE, ZONE_DISTANCE, ZONE_DISTANCE_M, ZONE_DISTANCE_M_EDGE, MAX_DISTANCE, CALC_DISTANCE, WAZE_DISTANCE, WAZE_METHOD, - TRAVEL_TIME, TRAVEL_TIME_MIN, DIR_OF_TRAVEL, MOVED_DISTANCE, + TRAVEL_TIME, TRAVEL_TIME_MIN, TRAVEL_TIME_HHMM, ARRIVAL_TIME, DIR_OF_TRAVEL, MOVED_DISTANCE, LAST_LOCATED, LAST_LOCATED_TIME, LAST_LOCATED_DATETIME, LAST_UPDATE, LAST_UPDATE_TIME, LAST_UPDATE_DATETIME, NEXT_UPDATE, NEXT_UPDATE_TIME, NEXT_UPDATE_DATETIME, @@ -50,7 +50,7 @@ log_info_msg, log_error_msg, log_exception, _trace, _traceha, ) from ..helpers.time_util import (secs_to_time, secs_to_time_str, secs_to_time_age_str, waze_mins_to_time_str, secs_since, time_to_12hrtime, secs_to_datetime, secs_to, secs_to_age_str, - datetime_now, time_now, time_now_secs, format_time_age, format_age_ts, ) + datetime_now, time_now, time_now_secs, secs_to_time_hhmm, secs_to_hhmm, ) from ..helpers.dist_util import (km_to_mi, km_to_mi_str, format_dist_km, format_dist_m, format_km_to_mi, ) @@ -460,6 +460,11 @@ def determine_interval(Device, DeviceFmZone): sensors[TRAVEL_TIME] = waze_mins_to_time_str(waze_time_from_zone) sensors[TRAVEL_TIME_MIN] = f"{waze_time_from_zone:.0f} min" + sensors[TRAVEL_TIME_HHMM] = secs_to_hhmm(waze_time_from_zone * 60) + if Device.is_inzone and Device.loc_data_zone == DeviceFmZone.from_zone: + sensors[ARRIVAL_TIME] =f"@{secs_to_time_hhmm(Device.zone_change_secs)}" + else: + sensors[ARRIVAL_TIME] = secs_to_time_hhmm(waze_time_from_zone * 60 + time_now_secs()) sensors[DIR_OF_TRAVEL] = dir_of_travel sensors[DISTANCE] = km_to_mi(dist_from_zone_km) diff --git a/custom_components/icloud3/support/start_ic3.py b/custom_components/icloud3/support/start_ic3.py index db02875..00aedc4 100644 --- a/custom_components/icloud3/support/start_ic3.py +++ b/custom_components/icloud3/support/start_ic3.py @@ -1874,8 +1874,11 @@ def setup_trackable_devices(): ''' # Cycle thru any paired devices and associate them with each otherthem with each other for PairedDevices in Gb.PairedDevices_by_paired_with_id.values(): - PairedDevices[0].PairedDevice = PairedDevices[1] - PairedDevices[1].PairedDevice = PairedDevices[0] + try: + PairedDevices[0].PairedDevice = PairedDevices[1] + PairedDevices[1].PairedDevice = PairedDevices[0] + except: + pass for devicename, Device in Gb.Devices_by_devicename.items(): Device.display_info_msg(f"Set Trackable Devices > {devicename}")