diff --git a/custom_components/icloud3/ChangeLog.txt b/custom_components/icloud3/ChangeLog.txt index a739e6c..aa2a785 100644 --- a/custom_components/icloud3/ChangeLog.txt +++ b/custom_components/icloud3/ChangeLog.txt @@ -2,12 +2,25 @@ - iCloud3 Documentation is [here](https://gcobb321.github.io/icloud3_v3_docs/#/chapters/0.1-introduction) - The *Installing iCloud3* chapter describes migrating from iCloud3 v2 and installing iCloud3 for the first time -### Change Log - v3.0.3 (4/14/2024) -1. ALERTS (New) - An alert message is displayed on the Event Log and in the _alert_ attribute on the device's device_tracker and badge entities until it has been resolved. Exaqmples of alerts are a startup error, no gps data, the device is offline, the battery below 20%, and tracking is paused. The alert attribute can be used in an automation to trigger sending a message to the device. See the Event Log chapter in the iCloud3 docs for more information and example automations. + + +v3.0.4 +....................... +### Change Log - v3.0.4 (not released) +1. ADD DEVICE (Fixed) - An 'Out of Range' error message was encountered adding the first device. +2. DIRECTION OF TRAVEL (Improvement) - Tweaked the AwayFrom direction override when approaching Home after the previous directions were Towards. +3. Event Type DEPRECIATED EOORO MESSAGE (Fixed) - This was a warning about the removal of the EventType from HA next year. It has been removed. +4. RAWDATA LOGGING - Changed some formatting of the log to better filter device messages. + + +v3.0.3 +....................... +### Change Log - v3.0.3 (5/1/2024) +1. ALERTS (New) - An alert message is displayed on the Event Log and in the _alert_ attribute on the device's device_tracker and badge entities until it has been resolved. Examples of alerts are a startup error, no gps data, the device is offline, the battery below 20%, and tracking is paused. The alert attribute can be used in an automation to trigger sending a message to any device using the Mobile App. See the _Reference > Devices and Other Alerts_ chapter in the iCloud3 docs [here](#/chapters/7.6-alerts) for more information and example automations. 2. BATTERY (Improvement) - The battery information attribute has been added to the device's device_tracker and badge entity. It shows the battery level, charging state, when the information was updated and the source of the data. The charging status text has been changed to 'Charged', 'Charging', 'NotCharging', 'Low' and 'Unknown'. 3. UPDATE SENSOR (Fixed) - An 'AttributeError' message has been fixed. It was caused by trying to update the sensor before the sensor had been set up. -4. CONFIGURE SETTINGS > ICLOUD ACCOUNT AND MOBILE APP screen (Fixed) - Changing iCloud account information (Username or password), logging into and saving the new account information was not being saved correctly so restarting iCloud3 would still use the old account information. Also added a LogOut option to log out of the iCloud Account. -5. DIRECTION OF TRAVEL (Improvement) - When Driving towards Home, the calculated straight line distance is used to determine the travel direction ('Towards'). The direction would momentairily change to 'AwayFrom' if the distance from Home increased due to a curve in the road or you were stopped at an intersection. It would then back to 'Towards' on the next update. In this case, the direction will now remain 'Towards'. +4. CONFIGURE SETTINGS > ICLOUD ACCOUNT AND MOBILE APP screen (Fixed) - Changing iCloud account information (Username or password) was not being saved correctly so restarting iCloud3 would still use the old account. A Log Off option was added to initialize the iCloud Account username/password fields. +5. DIRECTION OF TRAVEL (Improvement) - When Driving towards Home, the calculated straight line distance is used to determine the travel direction ('Towards'). The direction would momentarily change to 'AwayFrom' if the distance from Home increased due to a curve in the road or you were stopped at an intersection. It would then change back to 'Towards' on the next update. In this case, the direction will not be changed and will remain 'Towards'. 6. Other minor code changes, tuning and code cleanup. diff --git a/custom_components/icloud3/config_flow.py b/custom_components/icloud3/config_flow.py index 21c0e5b..8c0e986 100644 --- a/custom_components/icloud3/config_flow.py +++ b/custom_components/icloud3/config_flow.py @@ -2989,11 +2989,17 @@ def _is_only_non_tracked_field_updated(self, user_input): Restart iCloud3 if a tracked field was updated ''' - for pname, pvalue in user_input.items(): - if (Gb.conf_devices[self.conf_device_selected_idx][pname] != pvalue - and pname not in DEVICE_NON_TRACKING_FIELDS): + try: + if Gb.conf_devices == []: return False + for pname, pvalue in user_input.items(): + if (Gb.conf_devices[self.conf_device_selected_idx][pname] != pvalue + and pname not in DEVICE_NON_TRACKING_FIELDS): + return False + except: + return False + return True #------------------------------------------------------------------------------------------- @@ -3705,7 +3711,7 @@ def remove_track_fm_zone_sensor_entity(self, devicename, remove_tfz_zones_list): if remove_tfz_zones_list == []: return - device_tfz_sensors = Gb.Sensors_by_devicename_from_zone.get(devicename) + device_tfz_sensors = Gb.Sensors_by_devicename_from_zone.get(devicename).copy() if device_tfz_sensors is None: return diff --git a/custom_components/icloud3/const.py b/custom_components/icloud3/const.py index a587859..9f7d359 100644 --- a/custom_components/icloud3/const.py +++ b/custom_components/icloud3/const.py @@ -4,7 +4,7 @@ # #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -VERSION = '3.0.3' +VERSION = '3.0.4' #----------------------------------------- DOMAIN = 'icloud3' ICLOUD3 = 'iCloud3' @@ -252,7 +252,7 @@ dark_circled_letters = "๐Ÿ… ๐Ÿ…‘ ๐Ÿ…’ ๐Ÿ…“ ๐Ÿ…” ๐Ÿ…• ๐Ÿ…– ๐Ÿ…— ๐Ÿ…˜ ๐Ÿ…™ ๐Ÿ…š ๐Ÿ…› ๐Ÿ…œ ๐Ÿ… ๐Ÿ…ž ๐Ÿ…Ÿ ๐Ÿ…  ๐Ÿ…ก ๐Ÿ…ข ๐Ÿ…ฃ ๐Ÿ…ค ๐Ÿ…ฅ ๐Ÿ…ฆ ๐Ÿ…ง ๐Ÿ…จ ๐Ÿ…ฉ โœช" Symbols = ยฑโ–ชโ€ขโ—โ–ฌโฎพ โŠ— โŠ˜โœ“ร—รธยฆ โ–ถโ—€ โ–บโ—„โ–ฒโ–ผ โˆ™โ–ช ยปยซ oPhone=โ–บโ–ถโ†’โŸพโžคโžŸโžœโž”โžค๐Ÿก†๐Ÿกช๐ŸกบโŸน๐Ÿก†โž”แ…โ—ˆ๐Ÿฑโ˜’โ˜ขโ›’โŠ˜ฦŸโŠ—โ“งโ“โ›’๐Ÿœ” Important = โ—โŒโš ๏ธโ“๐Ÿ›‘โ›”โšกโญโญ•โ“˜โ€ข โฐ โ€ถโ€ณโ€œโ€โ€˜โ€™โ€ถโ€ณ ๐Ÿ•“ - โ€” โ€“แ—’ โƒ ยป โ”โ–ถ โ”โžค๐Ÿกบ โ€”> > > โฏโ†ฆ โ€ฆ ๐Ÿกชแ—• แ—’ แณ โ”€๐Ÿกข โ”€โ”€แ—’ ๐Ÿกข โ”€แ… โ†ฃ โž™ โ†’ใ€Šใ€‹โ—†โ—ˆโ—‰โ— + โ€” โ€“แ—’ โƒ ยป โ”โ–ถ โ”โžค๐Ÿกบ โ€”> > > โฏโ†ฆ โ€ฆ โ‹ฎ ๐Ÿกชแ—• แ—’ แณ โ”€๐Ÿกข โ”€โ”€แ—’ ๐Ÿกข โ”€แ… โ†ฃ โž™ โ†’ใ€Šใ€‹โ—†โ—ˆโ—‰โ— โ–โ€– โ–นโ–ปโ–ทโ—โ—…โ—ƒโ€–โ• แ…๐Ÿก†โ–ถโ–๐Ÿก†โ–โ–ถโ€–โžคโ–โžคโžœโž”โฐโฐโฑโฑ โ ค ยฒ โฃ‡โ ˆโ ‰โ ‹โ ›โ Ÿโ ฟโกฟโฃฟ https://www.fileformat.info/info/unicode/block/braille_patterns/utf8test.htm ''' diff --git a/custom_components/icloud3/device.py b/custom_components/icloud3/device.py index 063c34b..dfe36bb 100644 --- a/custom_components/icloud3/device.py +++ b/custom_components/icloud3/device.py @@ -1082,9 +1082,9 @@ def is_approaching_tracked_zone(self): if (secs_to(self.next_update_secs) <= 15 and secs_since(self.loc_data_secs > 15) and self.FromZone_TrackFrom.is_going_towards - and self.FromZone_TrackFrom.zone_dist < 1 and self.went_3km): return True + # and self.FromZone_TrackFrom.zone_dist < 1 return False @property @@ -1438,16 +1438,20 @@ def set_passthru_zone_delay(self, data_source, zone_entered=None, zone_entered_s False - Zone was reset and should proceed with an update ''' # Passthru zone is not used or already set up + passthru_not_used_reason = '' if (zone_entered == self.passthru_zone or zone_entered == self.loc_data_zone): return True # Entering a zone not subject to a delay - if (zone_entered in self.FromZones_by_zone - or is_statzone(zone_entered) - or zone_entered is None - or (data_source == ICLOUD and self.is_location_old_or_gps_poor)): - return False + if zone_entered in self.FromZones_by_zone: + passthru_not_used_reason = 'TrackedFrom Zone' + elif is_statzone(zone_entered): + passthru_not_used_reason = 'Stat Zone' + elif zone_entered is None: + passthru_not_used_reason = 'Unknown Zone' + elif (data_source == ICLOUD and self.is_location_old_or_gps_poor): + passthru_not_used_reason = 'Old Location' # Not set and next update not reached, set it below elif (self.is_passthru_timer_set is False @@ -1457,14 +1461,17 @@ def set_passthru_zone_delay(self, data_source, zone_entered=None, zone_entered_s # Time for an update, reset it elif self.is_next_update_time_reached: self.reset_passthru_zone_delay() - - return False + passthru_not_used_reason = 'Next Update Time Reached' # Passthru expire is set, if before enter zone time or this update time, reset it elif (self.is_passthru_timer_set and (zone_entered_secs > self.passthru_zone_timer or Gb.this_update_secs >= self.passthru_zone_timer)): self.reset_passthru_zone_delay() + passthru_not_used_reason = 'Timer Expired' + + if passthru_not_used_reason: + post_event(self.devicename, f"Zone Enter Not Delayed > {passthru_not_used_reason}") return False # Activate Passthru zone @@ -1902,9 +1909,9 @@ def update_battery_data_from_mobapp(self): return False battery_level = int(battery_level_attrs[STATE]) - if (mins_since(battery_update_secs) > 45 - or battery_level_attrs[LAST_UPDATED_SECS] != battery_level_attrs[LAST_CHANGED_SECS]): - log_rawdata(f"Mobile App Battery Level - {self.devicename}", battery_level_attrs) + if (Gb.this_update_time.endswith('00:00') + or battery_update_secs != self.mobapp_data_battery_update_secs): + log_rawdata(f"MobApp Battery Level - <{self.devicename}>", battery_level_attrs) if battery_level > 99: battery_status = 'Charged' @@ -2096,11 +2103,12 @@ def update_dev_loc_data_from_raw_data_FAMSHR_FMF(self, RawData, requesting_devic #------------------------------------------------------------------- def display_update_location_msg(self): + return if self.loc_data_time_gps == self.last_loc_data_time_gps: return if self.isnotin_zone or self.loc_data_dist_moved_km > .015: - event_msg =(f"SinceLast > " + event_msg =(f"Selected > " f"{self.last_loc_data_time_gps}" f"{RARROW}{self.dev_data_source}-{self.loc_data_time_gps}") post_event(self.devicename,event_msg) @@ -2154,6 +2162,7 @@ def update_sensor_values_from_data_fields(self): self.sensors[MOVED_TIME_TO] = self.loc_data_time_moved_to self.sensors[ZONE_DATETIME] = secs_to_datetime(self.zone_change_secs) + if self.FromZone_NextToUpdate is None: self.FromZone_NextToUpdate = self.FromZone_Home self.interval_secs = self.FromZone_NextToUpdate.interval_secs self.interval_str = self.FromZone_NextToUpdate.interval_str self.next_update_secs = self.FromZone_NextToUpdate.next_update_secs @@ -2162,6 +2171,7 @@ def update_sensor_values_from_data_fields(self): self.sensors[NEXT_UPDATE_TIME] = self.FromZone_NextToUpdate.sensors[NEXT_UPDATE_TIME] self.sensors[NEXT_UPDATE] = self.FromZone_NextToUpdate.sensors[NEXT_UPDATE] + if self.FromZone_TrackFrom is None: self.FromZone_TrackFrom = self.FromZone_Home self.sensors[FROM_ZONE] = self.FromZone_TrackFrom.from_zone self.sensors[LAST_UPDATE_DATETIME] = self.FromZone_TrackFrom.sensors[LAST_UPDATE_DATETIME] self.sensors[LAST_UPDATE_TIME] = self.FromZone_TrackFrom.sensors[LAST_UPDATE_TIME] @@ -2178,14 +2188,11 @@ def update_sensor_values_from_data_fields(self): self.sensors[WAZE_DISTANCE] = self.FromZone_TrackFrom.sensors[WAZE_DISTANCE] self.sensors[WAZE_METHOD] = self.FromZone_TrackFrom.sensors[WAZE_METHOD] self.sensors[CALC_DISTANCE] = self.FromZone_TrackFrom.sensors[CALC_DISTANCE] + self.sensors[HOME_DISTANCE] = self.FromZone_Home.sensors[ZONE_DISTANCE] self.FromZone_TrackFrom.dir_of_travel = dir_of_travel = \ self.FromZone_TrackFrom.sensors[DIR_OF_TRAVEL] - # if dir_of_travel == INZONE: - # self.sensors[DIR_OF_TRAVEL] = f"@{zone_dname(self.loc_data_zone)[:8]}" - # else: - # self.sensors[DIR_OF_TRAVEL] = dir_of_travel self.sensors[DIR_OF_TRAVEL] = dir_of_travel # Update the last zone info if the device was in a zone and now not in a zone or went immediatelly from @@ -2418,10 +2425,6 @@ def display_info_msg(self, info_msg=None, new_base_msg=False): # PassThru zone msg has priority over all other messages if self.is_passthru_zone_delay_active and instr(info_msg, 'PassThru') is False: return - # if new_base_msg is False: - # return - - #info_msg = info_msg if new_base_msg else f"ใ€Š{info_msg}ใ€‹{self.info_msg}" try: self.write_ha_sensor_state(INFO, info_msg) diff --git a/custom_components/icloud3/helpers/common.py b/custom_components/icloud3/helpers/common.py index 90860ff..44f8c1c 100644 --- a/custom_components/icloud3/helpers/common.py +++ b/custom_components/icloud3/helpers/common.py @@ -33,8 +33,8 @@ def list_to_str(list_value, separator=None): ''' if list_value == []: return '' separator_str = separator if separator else ', ' - if None in list_value: - list_value = [lv for lv in list_value if lv is not None] + if None in list_value or '' in list_value: + list_value = [lv for lv in list_value if lv is not None and lv != ''] list_str = separator_str.join(list_value) if list_value else 'None' if separator_str.startswith(CRLF): diff --git a/custom_components/icloud3/helpers/entity_io.py b/custom_components/icloud3/helpers/entity_io.py index 1eb1a23..9fa83bc 100644 --- a/custom_components/icloud3/helpers/entity_io.py +++ b/custom_components/icloud3/helpers/entity_io.py @@ -383,7 +383,7 @@ def trace_device_attributes(Device, description, fct_name, attrs): log_msg = (f"{description} Attrs-{trace_attrs}{trace_attrs_in_attrs}") log_debug_msg(Device.devicename, log_msg) - log_rawdata(f"iCloud Rawdata - {Device.devicename}--{description}", attrs) + log_rawdata(f"FamShr iCloud Rawdata - <{Device.devicename}> {description}", attrs) except Exception as err: pass diff --git a/custom_components/icloud3/helpers/messaging.py b/custom_components/icloud3/helpers/messaging.py index 66b890b..c6cc812 100644 --- a/custom_components/icloud3/helpers/messaging.py +++ b/custom_components/icloud3/helpers/messaging.py @@ -9,6 +9,7 @@ DASH_50, DASH_DOTTED_50, TAB_11, RED_ALERT, RED_STOP, RED_CIRCLE, YELLOW_ALERT, DATETIME_FORMAT, DATETIME_ZERO, NEXT_UPDATE_TIME, INTERVAL, + FAMSHR_FNAME, MOBAPP_FNAME, CONF_IC3_DEVICENAME, CONF_FNAME, CONF_LOG_LEVEL, CONF_PASSWORD, CONF_USERNAME, CONF_DEVICES, LATITUDE, LONGITUDE, LOCATION_SOURCE, TRACKING_METHOD, @@ -67,27 +68,31 @@ 'items', 'userInfo', 'prsId', 'dsid', 'dsInfo', 'webservices', 'locations', 'devices', 'content', 'followers', 'following', 'contactDetails', ] -# TABS_BOX_DEBUG = "\t\t\t\t\t\t\t\t\t\t " -# TABS_BOX_INFO = "\t\t\t\t\t " -# TABS_BOX_EVLOG_EXPORT = "\t\t\t" -SP50 = ' '*50 -SP = { - 4: SP50[1:4], - 5: SP50[1:5], - 6: SP50[1:6], - 8: SP50[1:8], - 9: SP50[1:9], - 10: SP50[1:10], - 11: SP50[1:11], - 12: SP50[1:12], - 16: SP50[1:16], - 22: SP50[1:22], - 28: SP50[1:28], - 26: SP50[1:26], - 44: SP50[1:44], - 48: SP50[1:48], - 50: SP50, + +SP_str = ' '*50 +SP_dict = { + 4: SP_str[1:4], + 5: SP_str[1:5], + 6: SP_str[1:6], + 8: SP_str[1:8], + 9: SP_str[1:9], + 10: SP_str[1:10], + 11: SP_str[1:11], + 12: SP_str[1:12], + 13: SP_str[1:13], + 14: SP_str[1:14], + 16: SP_str[1:16], + 22: SP_str[1:22], + 28: SP_str[1:28], + 26: SP_str[1:26], + 44: SP_str[1:44], + 48: SP_str[1:48], + 50: SP_str, } +def SP(space_cnt): + if space_cnt in SP_dict: return SP_dict[space_cnt] + if space_cnt < len(SP_str): return SP_str[1:space_cnt] + return ' '*space_cnt #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> # @@ -337,8 +342,8 @@ def check_ic3log_file_exists(ic3logger_file): open_ic3log_file(new_log_file=True) log_msg = f"{EVLOG_IC3_STARTING}Recreated iCloud3 Log File: {ic3logger_file}" - log_msg = f"{format_startup_header_box(log_msg)}" - log_msg = f"{format_header_box_indent(log_msg, 4).replace('โก‡', 'โ›”')}" + log_msg = f"{format_startup_header_box(log_msg, 20)}" + log_msg = log_msg.replace('โก‡', 'โ›”') Gb.iC3Logger.info(log_msg) return True @@ -386,7 +391,7 @@ def write_config_file_to_ic3log(): conf_tracking_recd[CONF_DEVICES] = f"{len(Gb.conf_devices)}" Gb.trace_prefix = '_INIT_' - indent = SP[44] if Gb.log_debug_flag else SP[26] + indent = SP(44) if Gb.log_debug_flag else SP(26) log_msg = ( f"iCloud3 v{Gb.version}, " f"{dt_util.now().strftime('%A')}, " f"{dt_util.now().strftime(DATETIME_FORMAT)[:19]}") @@ -439,7 +444,7 @@ def log_info_msg(module_name, log_msg='+'): log_msg = format_msg_line(log_msg) write_ic3log_recd(log_msg) - log_msg = log_msg.replace(' > +', f" > ...\n{SP[22]}+") + log_msg = log_msg.replace(' > +', f" > ...\n{SP(22)}+") Gb.HALogger.debug(log_msg) #-------------------------------------------------------------------- @@ -482,7 +487,7 @@ def log_debug_msg(devicename_or_Device, log_msg='+', msg_prefix=None): write_ic3log_recd(log_msg) - log_msg = log_msg.replace(' > +', f" > ...\n{SP[22]}+") + log_msg = log_msg.replace(' > +', f" > ...\n{SP(22)}+") Gb.HALogger.debug(log_msg) #-------------------------------------------------------------------- @@ -496,7 +501,7 @@ def log_start_finish_update_banner(start_finish, devicename, Device = Gb.Devices_by_devicename[devicename] text = (f"{devicename}, {method}, " f"CurrZone-{Device.sensor_zone}, {update_reason} ") - log_msg = format_header_box(text, start_finish) + log_msg = format_header_box(text, indent=43, start_finish=start_finish) log_info_msg(log_msg) @@ -534,7 +539,6 @@ def format_msg_line(log_msg, area=None): Gb.trace_prefix source = f"{_called_from()}{program_area}" log_msg = format_startup_header_box(log_msg) - log_msg = format_header_box_indent(log_msg, len(source)) msg_prefix= ' ' if log_msg.startswith('โก‡') else \ ' โก‡ ' if Gb.trace_group else \ ' ' @@ -553,9 +557,9 @@ def filter_special_chars(recd, evlog_export=False): Filter out EVLOG_XXX control fields ''' - indent =SP[16] if evlog_export else \ - SP[48] if Gb.log_debug_flag else \ - SP[28] + indent =SP(16) if evlog_export else \ + SP(48) if Gb.log_debug_flag else \ + SP(28) if recd.startswith('^'): recd = recd[3:] recd = recd.replace(EVLOG_MONITOR, '') @@ -608,13 +612,16 @@ def format_startup_header_box(log_msg): return log_msg #-------------------------------------------------------------------- -def format_header_box(recd, start_finish=None, evlog_export=False): +def format_header_box(recd, indent=None, start_finish=None, evlog_export=False): ''' Format a box around this item ''' start_pos = recd.find('^') if start_pos == -1: start_pos = 0 + # Default indent for icloud3-0.log file is 43 + if indent is None: indent = 43 + top_char = bot_char = DASH_50 if start_finish == 'start': bot_char = f"{'โ ‚'*37}" @@ -624,15 +631,8 @@ def format_header_box(recd, start_finish=None, evlog_export=False): Gb.trace_group = False return (f"โก‡{top_char}\n" - f"โ–นโก‡{SP[4]}{recd[start_pos:].upper()}\n" - f"โ–นโก‡{bot_char}") - -#-------------------------------------------------------------------- -def format_header_box_indent(log_msg, indent): - if instr(log_msg, 'โ–นโก‡') is False: - return log_msg - - return log_msg.replace('โ–น', f"{' '*(16+indent)}") + f"{SP(indent)}โก‡{SP(4)}{recd[start_pos:].upper()}\n" + f"{SP(indent)}โก‡{bot_char}") #------------------------------------------------------------------------------------------- def _resolve_devicename_log_msg(devicename_or_Device, event_msg): @@ -703,27 +703,38 @@ def log_rawdata(title, rawdata, log_rawdata_flag=False): if Gb.log_rawdata_flag is False or rawdata is None: return - if Gb.log_rawdata_flag_unfiltered: - log_rawdata_unfiltered(title, rawdata) - return + # log_info_msg(f"RAWDATA 706 {title=} {Gb.log_level_devices}") + # if Gb.log_rawdata_flag_unfiltered: + # log_rawdata_unfiltered(title, rawdata) + # return + + if (Gb.start_icloud3_inprocess_flag + or 'all' in Gb.log_level_devices + or Gb.log_level_devices == []): + pass + elif (Gb.log_level_devices + and (instr(title, FAMSHR_FNAME) + or instr(title, MOBAPP_FNAME) + or instr(title, 'iCloud') + or instr(title, 'Mobile'))): + + log_level_devices = [devicename for devicename in Gb.log_level_devices if instr(title, devicename)] + if log_level_devices == []: + return filtered_dicts = {} filtered_lists = {} - filtered_data = {} - rawdata_data = {} + filtered_data = {} + rawdata_data = {} try: if type(rawdata) is not dict: log_info_msg(f"{'โ”€'*8} {title.upper()} {'โ”€'*8}\n{rawdata}") return - elif 'all' in Gb.log_level_devices: - pass - else: - rawdata_ic3_devicename = \ - rawdata.get(CONF_IC3_DEVICENAME) or rawdata['filter'].get(CONF_IC3_DEVICENAME) - if rawdata_ic3_devicename not in Gb.log_level_devices: - log_info_msg(f"RawData for {rawdata_ic3_devicename} not logged") - return + + if Gb.log_rawdata_flag_unfiltered: + log_rawdata_unfiltered(title, rawdata) + return if 'raw' in rawdata or log_rawdata_flag: log_info_msg(f"{'โ”€'*8} {title.upper()} {'โ”€'*8}\n{rawdata}") diff --git a/custom_components/icloud3/sensor.py b/custom_components/icloud3/sensor.py index 082532c..d22c006 100644 --- a/custom_components/icloud3/sensor.py +++ b/custom_components/icloud3/sensor.py @@ -70,6 +70,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e # Save the hass `add_entities` call object for use in config_flow for adding new sensors Gb.hass = hass Gb.async_add_entities_sensor = async_add_entities + Gb.sensors_created_cnt = 0 try: if Gb.conf_file_data == {}: @@ -128,7 +129,6 @@ def create_tracked_device_sensors(devicename, conf_device, new_sensors_list=None ''' try: NewSensors = [] - Gb.sensors_created_cnt = 0 if new_sensors_list is None: new_sensors_list = [] diff --git a/custom_components/icloud3/support/determine_interval.py b/custom_components/icloud3/support/determine_interval.py index e20aa24..64faaa8 100644 --- a/custom_components/icloud3/support/determine_interval.py +++ b/custom_components/icloud3/support/determine_interval.py @@ -43,7 +43,7 @@ # from ..support import mobapp_interface # from ..support import stationary_zone as statzone -from ..helpers.common import (instr, round_to_zero, is_zone, is_statzone, isnot_zone, +from ..helpers.common import (instr, isbetween, round_to_zero, is_zone, is_statzone, isnot_zone, zone_dname, ) from ..helpers.messaging import (post_event, post_error_msg, post_evlog_greenbar_msg, clear_evlog_greenbar_msg, @@ -180,7 +180,7 @@ def determine_interval(Device, FromZone): Device.statzone_clear_timer waze_time_msg = 'NotUsed' - calc_interval_secs = round(km_to_mi(dist_from_zone_km) / 1.5) * 60 + calc_interval_secs = round(km_to_mi(dist_from_zone_km) * Gb.travel_time_factor) * 60 if Gb.Waze.is_status_USED: waze_interval_secs = round(waze_time_from_zone * 60 * Gb.travel_time_factor , 0) else: @@ -320,13 +320,15 @@ def determine_interval(Device, FromZone): interval_method = '3.Calc' interval_secs = calc_interval_secs - if (dir_of_travel in ('', ' ', '___', AWAY_FROM) - and interval_secs < 180 - and interval_secs > 30): - interval_method += '+6.AwayFm+<3min' - interval_secs = 180 + # if (dir_of_travel in ('', ' ', '___', AWAY_FROM) + # and isbetween(interval_secs, 30, 180)): + # interval_method += '+6.AwayFm+<3min' + # interval_secs = 180 - elif (dir_of_travel == AWAY_FROM + if (dir_of_travel == AWAY_FROM + and calc_dist_from_zone_km >= 3 + and Device.state_change_flag is False + and Device.is_gps_good and not Gb.Waze.distance_method_waze_flag and Device.fixed_interval_secs == 0): interval_method += '+6.AwayFm+Calc' @@ -349,12 +351,12 @@ def determine_interval(Device, FromZone): interval_secs = 180 #if changed zones on this poll reset multiplier - if Device.state_change_flag: - interval_multiplier = 1 + # if Device.state_change_flag: + # interval_multiplier = 1 #Check accuracy again to make sure nothing changed, update counter - if Device.is_gps_poor: - interval_multiplier = 1 + # if Device.is_gps_poor: + # interval_multiplier = 1 try: #Real close, final check to make sure interval_secs is not adjusted @@ -396,6 +398,12 @@ def determine_interval(Device, FromZone): interval_method = "9.Max" interval_secs = Gb.max_interval_secs + # if moving, make sure interval is not larger than the travtime*factor + if (dir_of_travel in [AWAY_FROM, TOWARDS] + and waze_interval_secs > 0 + and interval_secs > (waze_interval_secs * (Gb.travel_time_factor * 1.5))): + interval_secs = waze_interval_secs * Gb.travel_time_factor + interval_str = format_timer(interval_secs) if interval_multiplier > 1: @@ -551,11 +559,13 @@ def post_results_message_to_event_log(Device, FromZone): event_msg += f"Arrive-{FromZone.sensors[ARRIVAL_TIME]}, " event_msg += f"NextUpdate-{FromZone.next_update_time}, " + event_msg += f"Moved-{km_to_um(Device.loc_data_dist_moved_km)} " - if Device.isnotin_zone: + if Device.isin_zone: + event_msg += ', ' + else: awayfrom_override_star = '*' if FromZone.dir_of_travel_awayfrom_override else '' - event_msg += (f"Moved-{km_to_um(Device.loc_data_dist_moved_km)} " - f"({FromZone.dir_of_travel}{awayfrom_override_star}), ") + event_msg += f"({FromZone.dir_of_travel}{awayfrom_override_star}), " if Device.is_statzone_timer_set and Device.is_tracked and Gb.is_statzone_used: event_msg += f"IntoStatZone-{secs_to_time(Device.statzone_timer)}, " @@ -578,23 +588,25 @@ def post_results_message_to_event_log(Device, FromZone): post_event(Device, event_msg[:-2]) log_msg = ( f"RESULTS: From-{FromZone.from_zone_dname} > " - f"MobAppZone-{Device.mobapp_data_state}, " - f"iC3Zone-{Device.loc_data_zone}, " - f"Interval-{FromZone.interval_str}, " + f"MobApp-{Device.mobapp_data_state}, " + f"iC3-{Device.loc_data_zone}, " + f"Intrvl-{FromZone.interval_str}, " f"TravTime-{FromZone.last_travel_time}, " - f"Dist-{km_to_um(FromZone.zone_dist)}, " - f"NextUpdt-{FromZone.next_update_time}, " - f"MaxDist-{km_to_um(FromZone.max_dist_km)}, " - f"Dir-{FromZone.dir_of_travel}, " + f"Dist-{format_dist_km(FromZone.zone_dist)}, " + f"MaxDist-{format_dist_km(FromZone.max_dist_km)}, " f"Moved-{format_dist_km(Device.statzone_dist_moved_km)}, " - f"Battery-{Device.dev_data_battery_level}%, " - f"LastDataUpdate-{secs_to_time(Device.last_data_update_secs)}, " - f"GPSAccuracy-{Device.loc_data_gps_accuracy}m, " + f"Calc/WazeDist={FromZone.sensors[CALC_DISTANCE]}/{FromZone.sensors[WAZE_DISTANCE]}, " + f"Dir-{FromZone.dir_of_travel}, " + f"Batt-{Device.dev_data_battery_level}%, " + f"NextUpdt-{FromZone.next_update_time}, " + f"LastUpdt-{secs_to_time(Device.last_data_update_secs)}, " + f"GPSAccur-{Device.loc_data_gps_accuracy}m, " f"LocAge-{format_age(Device.loc_data_secs)}, " - f"OldThreshold-{format_timer(Device.old_loc_threshold_secs)}, " + f"OldThresh-{format_timer(Device.old_loc_threshold_secs)}, " f"LastEvLogMsg-{secs_to_time(Device.last_evlog_msg_secs)}, " f"Method-{FromZone.interval_method}") - log_info_msg(Device.devicename, log_msg) + #log_info_msg(Device, log_msg) + post_monitor_msg(Device, log_msg) #-------------------------------------------------------------------------------- def post_zone_time_dist_event_msg(Device, FromZone): @@ -609,7 +621,7 @@ def post_zone_time_dist_event_msg(Device, FromZone): mobapp_state = 'NotUsed' else: mobapp_state = zone_dname(Device.mobapp_data_state) - ic3_zone = zone_dname(Device.loc_data_zone) + ic3_zone = zone_dname(Device.loc_data_zone) if Device.loc_data_zone == NOT_SET: interval_str = travel_time = 0 @@ -1028,9 +1040,6 @@ def _get_distance_data(Device, FromZone): time_change_secs = 0 if waze_time_from_zone == 0 \ else int(waze_time_from_zone * 60) - int(FromZone.waze_time * 60) dist_from_zone_moved_m = int(dist_from_zone_m - FromZone.zone_dist_m) - dist_from_zone_moved_m = round_to_zero(dist_from_zone_moved_m) - if abs(dist_from_zone_moved_m) < FromZone.zone_dist_m * .1: - dist_from_zone_moved_m = 0 if Device.isin_zone: dir_of_travel = INZONE_HOME if Device.loc_data_zone == HOME else \ @@ -1044,8 +1053,8 @@ def _get_distance_data(Device, FromZone): elif time_change_secs == 0 and dist_from_zone_moved_m == 0: dir_of_travel = Device.sensors[DIR_OF_TRAVEL] - # Far Away if dist > 400km/250mi - elif (calc_dist_from_zone_km > 90): + # Far Away if dist > 150km/100mi + elif (calc_dist_from_zone_km > 150): dir_of_travel = FAR_AWAY # Towards if the last zone distance > than this zone distance @@ -1067,7 +1076,7 @@ def _get_distance_data(Device, FromZone): ['TTT', 'TTt', 'TtT', 'tTT', 'Ttt', 'ttT']): dir_of_travel = TOWARDS dir_of_travel_awayfrom_override = True - _traceha(f"{dir_of_travel_awayfrom_override=}") + else: dir_of_travel_awayfrom_override = False @@ -1230,7 +1239,7 @@ def copy_near_device_results(Device, FromZone): Device.statzone_dist_moved_km = NearDevice.statzone_dist_moved_km Device.mobapp_request_loc_sent_secs = Gb.this_update_secs - log_rawdata(f"{Device.devicename} - {from_zone}", FromZone.sensors) + log_rawdata(f"{Device.data_source} - <{Device.devicename}> - {from_zone}", FromZone.sensors) return FromZone.sensors diff --git a/custom_components/icloud3/support/event_log.py b/custom_components/icloud3/support/event_log.py index 908dd8c..b33be5f 100644 --- a/custom_components/icloud3/support/event_log.py +++ b/custom_components/icloud3/support/event_log.py @@ -28,7 +28,7 @@ from ..helpers.common import instr, circle_letter, str_to_list, list_to_str, isbetween from ..helpers.messaging import (SP, log_exception, log_info_msg, log_warning_msg, _traceha, _trace, - filter_special_chars, format_header_box, format_header_box_indent, ) + filter_special_chars, format_header_box, ) from ..helpers.time_util import (time_to_12hrtime, datetime_now, time_now_secs, datetime_for_filename, adjust_time_hour_value, adjust_time_hour_values, ) @@ -845,17 +845,17 @@ def export_event_log(self): try: log_update_time = (f"{dt_util.now().strftime('%a, %m/%d')}, " f"{dt_util.now().strftime(Gb.um_time_strfmt)}") - hdr_recd = f"Time{SP[8]}Event\n{'-'*120}\n" + hdr_recd = f"Time{SP(8)}Event\n{'-'*120}\n" export_recd = (f"iCloud3 Event Log v{Gb.version}\n\n" f"Log Update Time: {log_update_time}\n" f"Tracked Devices:\n") export_recd += f"\nGeneral Configuration:\n" - export_recd += f"{SP[4]}{Gb.conf_general}\n" + export_recd += f"{SP(4)}{Gb.conf_general}\n" for devicename, Device in Gb.Devices_by_devicename.items(): - export_recd += (f"{SP[4]}{DOT}{Device.fname_devicename} >\n" - f"{SP[4]}{Device.conf_device}\n") + export_recd += (f"{SP(4)}{DOT}{Device.fname_devicename} >\n" + f"{SP(4)}{Device.conf_device}\n") #-------------------------------- # # Prepare Global '*' records. Reverse the list elements using [::-1] and make a string of the results @@ -914,26 +914,43 @@ def _export_ic3_event_log_reformat_recds(self, log_section, el_recds): record_str = '' startup_recds_flag = False el_recds.reverse() + last_tfz_zone = '' for record in el_recds: devicename = record[ELR_DEVICENAME] - time = record[ELR_TIME] if record[ELR_TIME] not in ['Debug', 'Rawdata'] else SP[4] + time = record[ELR_TIME] if record[ELR_TIME] not in ['Debug', 'Rawdata'] else SP(4) text = record[ELR_TEXT] + # iCloud3 Startup Records if log_section == 'startup': if text[0:3] in [EVLOG_IC3_STARTING, EVLOG_IC3_STAGE_HDR]: - text = f"{SP[9]}{format_header_box(text[3:], evlog_export=True)}" - text = format_header_box_indent(text, -5) + text = f"{SP(9)}{format_header_box(text[3:], indent=12, evlog_export=True)}" elif text.startswith('^'): - text = f"{SP[4]}{filter_special_chars(text[3:], evlog_export=True)}" + text = f"{SP(4)}{filter_special_chars(text[3:], evlog_export=True)}" else: - text = f"{SP[4]}{filter_special_chars(text, evlog_export=True)}" + text = f"{SP(4)}{filter_special_chars(text, evlog_export=True)}" + # Non-device related Records elif log_section == 'other': - text = f" {filter_special_chars(text, evlog_export=True)}" + text = f"{filter_special_chars(text, evlog_export=True)}" + # Device Records else: + time = (time + SP(8))[:8] text = self._reformat_device_recd(log_section, time, text) + if time.startswith('ยปHome'): + pass + + # Start of tfz group header + elif time.startswith('ยป') and time != last_tfz_zone: + text = f"{SP(4)}โก‡{' ~ '*18}\n{time}{text}" + last_tfz_zone = time + + # End of tfz group trailer + elif last_tfz_zone.startswith('ยป') and time != last_tfz_zone: + text = f"{last_tfz_zone}{SP(4)}โก‡{' ~ '*18}\n{time}{text}" + time = last_tfz_zone = '' + if text != '': record_str += f"{time}{text}\n" @@ -949,41 +966,36 @@ def _reformat_device_recd(self, log_section, time, text): # Time-record = {mobapp_state},{ic3_zone},{interval},{travel_time},{distance) - line_prefix = SP[4] + if text.startswith(EVLOG_UPDATE_START): text = f"Tracking Update ({log_section})" if text[3:] == '' else text[3:] - text = f"{SP[11]}{format_header_box(text, start_finish='start', evlog_export=True)}" - text = f"{format_header_box_indent(text, -5)}" + text = f"{SP(5)}{format_header_box(text, indent=12, start_finish='start', evlog_export=True)}" return text elif text.startswith(EVLOG_UPDATE_END): - text = f"{SP[9]}{format_header_box(text[3:], start_finish='finish', evlog_export=True)}" - text = f"{format_header_box_indent(text, -5)}" - line_prefix = '' + text = f"{SP(4)}{format_header_box(text[3:], indent=12, start_finish='finish', evlog_export=True)}" return text elif text.startswith(EVLOG_TIME_RECD): + tfz_adj = ' ' if (time.startswith('ยป') and time.startswith('ยปHome')) is False else '' text = text[3:] item = text.split(',') - text = (f"{' '*(11-len(time))}โก‡ MobApp-{item[0]}, " + text = (f"{' '*(11-len(time))}{tfz_adj}โก‡ " + f"MobApp-{item[0]}, " f"iCloud3-{item[1]}, " f"Interval-{item[2]}, " f"TravTime-{item[3]}, " f"Dist-{item[4]}") return text - if time.startswith('ยป'): line_prefix = SP[5] - + tfz_adj = ' ' if time.startswith('ยป') else '' group_char= '' if text.startswith('โก‡') else \ 'โก‡ ' if Gb.trace_group else \ '' - text = filter_special_chars(text) - - if instr(text, 'Results:') and instr(text, 'From-Home') is False: - text = f"{text}\n{SP[12]}โก‡ {' ~ '*18}" + text = filter_special_chars(text, evlog_export=True) - return f"{line_prefix}{group_char}{text}" + return f"{SP(4)}{tfz_adj}{group_char}{text}" #-------------------------------------------------------------------- @staticmethod diff --git a/custom_components/icloud3/support/icloud_data_handler.py b/custom_components/icloud3/support/icloud_data_handler.py index 633fb76..0a72b32 100644 --- a/custom_components/icloud3/support/icloud_data_handler.py +++ b/custom_components/icloud3/support/icloud_data_handler.py @@ -306,7 +306,7 @@ def update_device_with_latest_raw_data(Device, all_devices=False): except Exception as err: rawdata_msg = 'No Location data' if _RawData: - log_rawdata(f"{rawdata_msg}-{_Device.devicename}/{_Device.is_data_source_FAMSHR_FMF}", + log_rawdata(f"iCloud - {rawdata_msg}-{_Device.devicename}/{_Device.is_data_source_FAMSHR_FMF}", {'filter': _RawData.device_data}) continue # log_exception(err) diff --git a/custom_components/icloud3/support/mobapp_data_handler.py b/custom_components/icloud3/support/mobapp_data_handler.py index 4466abe..5eb2b6a 100644 --- a/custom_components/icloud3/support/mobapp_data_handler.py +++ b/custom_components/icloud3/support/mobapp_data_handler.py @@ -484,7 +484,7 @@ def update_mobapp_data_from_entity_attrs(Device, device_trkr_attrs): if Device.mobapp_data_secs >= mobapp_data_secs or gps_accuracy > Gb.gps_accuracy_threshold: return - log_rawdata(f"Mobile App - {Device.devicename}", device_trkr_attrs) + log_rawdata(f"MobApp Attrs - <{Device.devicename}>", device_trkr_attrs) Device.mobapp_data_state = device_trkr_attrs.get(DEVICE_TRACKER, NOT_SET) Device.mobapp_data_state_secs = device_trkr_attrs.get(f"state_{TIMESTAMP_SECS}", 0) diff --git a/custom_components/icloud3/support/mobapp_interface.py b/custom_components/icloud3/support/mobapp_interface.py index f2f2a8e..a667b96 100644 --- a/custom_components/icloud3/support/mobapp_interface.py +++ b/custom_components/icloud3/support/mobapp_interface.py @@ -64,7 +64,7 @@ def get_entity_registry_mobile_app_devices(): f"{dev_trkr_entity['entity_id']}") post_evlog_greenbar_msg(alert_msg) - log_title = (f"mobapp entity_registry entry -- {mobapp_devicename})") + log_title = (f"MobApp entity_registry entry - <{mobapp_devicename}>)") log_rawdata(log_title, dev_trkr_entity, log_rawdata_flag=True) raw_model = 'Unknown' @@ -73,7 +73,7 @@ def get_entity_registry_mobile_app_devices(): # Get raw_model from HA device_registry device_reg_data = device_registry.async_get(device_id) - log_title = (f"mobapp device_registry entry -- {mobapp_devicename})") + log_title = (f"MobApp device_registry entry - <{mobapp_devicename}>)") log_rawdata(log_title, str(device_reg_data), log_rawdata_flag=True) raw_model = device_reg_data.model diff --git a/custom_components/icloud3/support/pyicloud_ic3.py b/custom_components/icloud3/support/pyicloud_ic3.py index 054fa33..a24650d 100644 --- a/custom_components/icloud3/support/pyicloud_ic3.py +++ b/custom_components/icloud3/support/pyicloud_ic3.py @@ -2121,7 +2121,7 @@ def __init__(self, device_id, self.evlog_alert_char= '' self.Device = Gb.Devices_by_icloud_device_id.get(device_id) - self.ic3_devicename = '' + self.ic3_devicename = self.Device.devicename if self.Device else '' self.update_secs = time_now_secs() self.location_secs = 0 self.location_time = HHMMSS_ZERO @@ -2139,7 +2139,7 @@ def __init__(self, device_id, self.set_located_time_battery_info() self.device_data[DATA_SOURCE] = self.data_source - self.device_data[CONF_IC3_DEVICENAME] = self.ic3_devicename + self.device_data[CONF_IC3_DEVICENAME] = self.ic3_devicename #---------------------------------------------------------------------- @@ -2326,7 +2326,8 @@ def is_location_data_available(self): def set_located_time_battery_info(self): try: - self.device_data[CONF_IC3_DEVICENAME] = self.devicename + self.device_data[CONF_IC3_DEVICENAME] = self.ic3_devicename + if self.is_location_data_available: self.device_data[LOCATION][TIMESTAMP] = int(self.device_data[LOCATION][self.timestamp_field] / 1000) self.device_data[LOCATION][LOCATION_TIME] = secs_to_time(self.device_data[LOCATION][TIMESTAMP]) diff --git a/custom_components/icloud3/support/start_ic3.py b/custom_components/icloud3/support/start_ic3.py index 134940c..03b7139 100644 --- a/custom_components/icloud3/support/start_ic3.py +++ b/custom_components/icloud3/support/start_ic3.py @@ -85,7 +85,6 @@ import traceback from datetime import timedelta, date, datetime from collections import OrderedDict -from homeassistant.helpers.typing import ConfigType, EventType from homeassistant.helpers import event from homeassistant.core import Event, HomeAssistant, ServiceCall, State, callback from homeassistant.util import slugify @@ -140,7 +139,6 @@ def process_config_flow_parameter_updates(): Gb.config_flow_updated_parms = {''} event_msg =(f"Configuration Loading > " - f"Type-{config_flow_updated_parms}, " f"Type-{list_to_str(config_flow_updated_parms).title()}") post_event(event_msg) @@ -163,8 +161,8 @@ def process_config_flow_parameter_updates(): check_ic3_event_log_file_version() Gb.EvLog.setup_event_log_trackable_device_info() - stage_title = f'Configuration Changes Loaded' - post_event(f"{EVLOG_IC3_STAGE_HDR}{stage_title}") + # stage_title = f'Configuration Changes Loaded' + # post_event(f"{EVLOG_IC3_STAGE_HDR}{stage_title}") if 'reauth' in config_flow_updated_parms: Gb.evlog_action_request = CMD_RESET_PYICLOUD_SESSION diff --git a/custom_components/icloud3/support/start_ic3_control.py b/custom_components/icloud3/support/start_ic3_control.py index 478c402..19ac422 100644 --- a/custom_components/icloud3/support/start_ic3_control.py +++ b/custom_components/icloud3/support/start_ic3_control.py @@ -395,7 +395,9 @@ def stage_7_initial_locate(): else: continue - post_event(Device, 'Trigger > Initial Locate') + event_msg =(f"{Device.dev_data_source} Trigger > Initial Locate@" + f"{Device.loc_data_time_gps}") + post_event(Device, event_msg) if Device.no_location_data: event_msg = f"{EVLOG_ALERT}NO GPS DATA RETURNED FROM ICLOUD LOCATION SERVICE" diff --git a/custom_components/icloud3/support/zone_handler.py b/custom_components/icloud3/support/zone_handler.py index 4dc794e..d47fa70 100644 --- a/custom_components/icloud3/support/zone_handler.py +++ b/custom_components/icloud3/support/zone_handler.py @@ -12,7 +12,6 @@ import os import homeassistant.util.dt as dt_util -from homeassistant.helpers.typing import EventType from homeassistant.helpers import event from homeassistant.core import callback @@ -205,6 +204,7 @@ def select_zone(Device, latitude=None, longitude=None): zones_distance_list = \ [(f"{int(zone_data[ZD_DIST_M]):08}|{zone_data[ZD_NAME]}|{zone_data[ZD_DIST_M]}") for zone_data in zones_data if zone_data[ZD_NAME] != zone_selected] + zones_distance_list.sort() return ZoneSelected, zone_selected, zone_selected_dist_m, zones_distance_list @@ -225,7 +225,6 @@ def post_zone_selected_msg(Device, ZoneSelected, zone_selected, # Format distance msg zones_dist_msg = '' zones_displayed = [zone_selected] - zones_distance_list.sort() for zone_distance_list in zones_distance_list: zdl_items = zone_distance_list.split('|') _zone = zdl_items[1] @@ -233,7 +232,6 @@ def post_zone_selected_msg(Device, ZoneSelected, zone_selected, zones_dist_msg += ( f"{zone_dname(_zone)}" f"-{m_to_um(_zone_dist)}") - # zones_dist_msg += f"-r{int(Gb.Zones_by_zone[_zone].radius_m)}m" zones_dist_msg += ", " gps_accuracy_msg = '' @@ -468,7 +466,7 @@ def request_update_devices_no_mobapp_same_zone_on_exit(Device): #------------------------------------------------------------------------------ @callback -def ha_added_zone_entity_id(event): #EventType[event.EventStateChangedData]) -> None: +def ha_added_zone_entity_id(event): """Add zone entity ID.""" zone_entity_id = event.data['entity_id'] @@ -489,7 +487,7 @@ def ha_added_zone_entity_id(event): #EventType[event.EventStateChangedData]) -> #------------------------------------------------------------------------------ @callback -def ha_removed_zone_entity_id(event): #EventType[event.EventStateChangedData]) -> None: +def ha_removed_zone_entity_id(event): """Remove zone entity ID.""" try: zone_entity_id = event.data['entity_id']