Add reverse geocoding location support and fix NoneType error
- Add location template inputs (common_location_template, location_if_unique_template, location_format)
- Display common location in header when all assets share the same location
- Show per-asset location when locations differ using {location_if_unique}
- Location only shown when all three fields (city, state, country) are present
- Fix TypeError on NoneType length check when asset.created_at is null
- Add defensive checks for date parsing to prevent template errors
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -71,6 +71,9 @@ Each item in `added_assets` contains:
|
||||
| `playback_url` | Video playback URL (for VIDEO assets only, if shared link exists) |
|
||||
| `photo_url` | Photo preview URL (for IMAGE assets only, if shared link exists) |
|
||||
| `people` | List of people detected in this specific asset |
|
||||
| `city` | City name from reverse geocoding (if available) |
|
||||
| `state` | State/region name from reverse geocoding (if available) |
|
||||
| `country` | Country name from reverse geocoding (if available) |
|
||||
|
||||
## Message Template Variables
|
||||
|
||||
@@ -86,6 +89,7 @@ All message templates support these placeholder variables (use single braces):
|
||||
| `{assets}` | Formatted list of added assets (using asset item template) |
|
||||
| `{video_warning}` | Warning about video size limits (Telegram only, empty otherwise) |
|
||||
| `{common_date}` | Common date formatted with template if all assets share same date, empty otherwise |
|
||||
| `{common_location}` | Common location formatted with template if all assets share same location (requires city, state, country), empty otherwise |
|
||||
|
||||
## Asset Item Template Variables
|
||||
|
||||
@@ -107,6 +111,11 @@ These variables can be used in the image and video asset templates. Also used fo
|
||||
| `{album_name}` | Name of the album |
|
||||
| `{is_favorite}` | Favorite indicator (using template) if asset is favorite, empty otherwise |
|
||||
| `{rating}` | User rating (1-5) or empty if not rated |
|
||||
| `{location}` | Formatted location string (using location format template), empty if location data incomplete |
|
||||
| `{location_if_unique}` | Location formatted with template if locations differ between assets, empty if all same or data incomplete |
|
||||
| `{city}` | City name from reverse geocoding, empty if not available |
|
||||
| `{state}` | State/region name from reverse geocoding, empty if not available |
|
||||
| `{country}` | Country name from reverse geocoding, empty if not available |
|
||||
|
||||
## Telegram Media Attachments
|
||||
|
||||
|
||||
@@ -147,8 +147,8 @@ blueprint:
|
||||
name: "Assets Added Message"
|
||||
description: >
|
||||
Message sent when assets are added to an album.
|
||||
Variables: `{album_name}`, `{album_url}`, `{added_count}`, `{people}`, `{assets}`, `{video_warning}`, `{common_date}`
|
||||
default: "📷 {added_count} new photo(s) added to album \"{album_name}\".{people}{assets}{video_warning}"
|
||||
Variables: `{album_name}`, `{album_url}`, `{added_count}`, `{people}`, `{assets}`, `{video_warning}`, `{common_date}`, `{common_location}`
|
||||
default: "📷 {added_count} new photo(s) added to album \"{album_name}\"{common_date}{common_location}.{people}{assets}{video_warning}"
|
||||
selector:
|
||||
text:
|
||||
multiline: true
|
||||
@@ -276,6 +276,37 @@ blueprint:
|
||||
selector:
|
||||
text:
|
||||
|
||||
common_location_template:
|
||||
name: "Common Location Template"
|
||||
description: >
|
||||
Template for showing the common location in the header message when all assets
|
||||
share the same location. Only used when ALL location fields (city, state, country)
|
||||
are present for all assets. Use `{location}` as the placeholder.
|
||||
Leave empty to not show common location in header.
|
||||
default: " in {location}"
|
||||
selector:
|
||||
text:
|
||||
|
||||
location_if_unique_template:
|
||||
name: "Location If Unique Template"
|
||||
description: >
|
||||
Template for showing location in asset items when assets have different locations.
|
||||
Only used when ALL location fields (city, state, country) are present.
|
||||
Use `{location}` as the placeholder.
|
||||
Leave empty to not show per-asset locations.
|
||||
default: " 📍 {location}"
|
||||
selector:
|
||||
text:
|
||||
|
||||
location_format:
|
||||
name: "Location Format"
|
||||
description: >
|
||||
Format for displaying asset locations. Available placeholders:
|
||||
`{city}`, `{state}`, `{country}`. Only shown when all three fields are present.
|
||||
default: "{city}, {country}"
|
||||
selector:
|
||||
text:
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Telegram Media Attachments
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -788,6 +819,9 @@ variables:
|
||||
common_date_template: !input common_date_template
|
||||
date_if_unique_template: !input date_if_unique_template
|
||||
favorite_indicator_template: !input favorite_indicator_template
|
||||
common_location_template: !input common_location_template
|
||||
location_if_unique_template: !input location_if_unique_template
|
||||
location_format: !input location_format
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Event Data
|
||||
@@ -843,8 +877,8 @@ variables:
|
||||
unique_dates: >
|
||||
{% set ns = namespace(dates = []) %}
|
||||
{% for asset in filtered_assets %}
|
||||
{% set raw_date = asset.created_at | default('') %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %}
|
||||
{% set raw_date = asset.created_at | default('', true) %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date is string and raw_date | length > 0 else none %}
|
||||
{% set formatted_date = dt.strftime(date_format) if dt else '' %}
|
||||
{% if formatted_date | length > 0 and formatted_date not in ns.dates %}
|
||||
{% set ns.dates = ns.dates + [formatted_date] %}
|
||||
@@ -863,6 +897,46 @@ variables:
|
||||
{{ '' }}
|
||||
{% endif %}
|
||||
|
||||
# Compute unique locations from filtered assets (only when all location fields present)
|
||||
unique_locations: >
|
||||
{% set ns = namespace(locations = []) %}
|
||||
{% for asset in filtered_assets %}
|
||||
{% set city = asset.city | default('') %}
|
||||
{% set state = asset.state | default('') %}
|
||||
{% set country = asset.country | default('') %}
|
||||
{% if city | length > 0 and state | length > 0 and country | length > 0 %}
|
||||
{% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) %}
|
||||
{% if formatted_location not in ns.locations %}
|
||||
{% set ns.locations = ns.locations + [formatted_location] %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ ns.locations }}
|
||||
|
||||
# Check if all assets with location data share the same location
|
||||
all_locations_same: "{{ unique_locations | length == 1 }}"
|
||||
|
||||
# Check if all filtered assets have complete location data
|
||||
all_assets_have_location: >
|
||||
{% set ns = namespace(count = 0) %}
|
||||
{% for asset in filtered_assets %}
|
||||
{% set city = asset.city | default('') %}
|
||||
{% set state = asset.state | default('') %}
|
||||
{% set country = asset.country | default('') %}
|
||||
{% if city | length > 0 and state | length > 0 and country | length > 0 %}
|
||||
{% set ns.count = ns.count + 1 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{{ ns.count == filtered_assets | length and filtered_assets | length > 0 }}
|
||||
|
||||
# Common location for header (formatted with template if all same, empty otherwise)
|
||||
common_location: >
|
||||
{% if all_assets_have_location and unique_locations | length == 1 %}
|
||||
{{ common_location_template | replace('{location}', unique_locations[0]) }}
|
||||
{% else %}
|
||||
{{ '' }}
|
||||
{% endif %}
|
||||
|
||||
# Format assets list for notification
|
||||
assets_list: >
|
||||
{% if include_asset_details and filtered_assets | length > 0 %}
|
||||
@@ -871,12 +945,18 @@ variables:
|
||||
{% set assets_to_show = filtered_assets[:max_items] %}
|
||||
{% for asset in assets_to_show %}
|
||||
{% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %}
|
||||
{% set raw_date = asset.created_at | default('') %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %}
|
||||
{% set raw_date = asset.created_at | default('', true) %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date is string and raw_date | length > 0 else none %}
|
||||
{% set formatted_date = dt.strftime(date_format) if dt else '' %}
|
||||
{% set created_if_unique = '' if unique_dates | length == 1 else (date_if_unique_template | replace('{date}', formatted_date)) %}
|
||||
{% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %}
|
||||
{% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %}
|
||||
{% set city = asset.city | default('') %}
|
||||
{% set state = asset.state | default('') %}
|
||||
{% set country = asset.country | default('') %}
|
||||
{% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %}
|
||||
{% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %}
|
||||
{% set location_if_unique = '' if (not has_full_location) or (all_assets_have_location and unique_locations | length == 1) else (location_if_unique_template | replace('{location}', formatted_location)) %}
|
||||
{% set item = asset_template
|
||||
| replace('{filename}', asset.filename | default('Unknown'))
|
||||
| replace('{description}', asset.description | default(''))
|
||||
@@ -891,7 +971,12 @@ variables:
|
||||
| replace('{people}', (asset.people | default([])) | join(', '))
|
||||
| replace('{album_name}', event_album_name)
|
||||
| replace('{is_favorite}', is_favorite)
|
||||
| replace('{rating}', rating) %}
|
||||
| replace('{rating}', rating)
|
||||
| replace('{location}', formatted_location)
|
||||
| replace('{location_if_unique}', location_if_unique)
|
||||
| replace('{city}', city)
|
||||
| replace('{state}', state)
|
||||
| replace('{country}', country) %}
|
||||
{% set ns.items = ns.items ~ item %}
|
||||
{% endfor %}
|
||||
{% set more_count = filtered_assets | length - max_items %}
|
||||
@@ -1164,11 +1249,16 @@ action:
|
||||
{% set ns = namespace(items = '') %}
|
||||
{% for asset in fetched_assets %}
|
||||
{% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %}
|
||||
{% set raw_date = asset.created_at | default('') %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %}
|
||||
{% set raw_date = asset.created_at | default('', true) %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date is string and raw_date | length > 0 else none %}
|
||||
{% set formatted_date = dt.strftime(date_format) if dt else '' %}
|
||||
{% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %}
|
||||
{% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %}
|
||||
{% set city = asset.city | default('') %}
|
||||
{% set state = asset.state | default('') %}
|
||||
{% set country = asset.country | default('') %}
|
||||
{% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %}
|
||||
{% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %}
|
||||
{% set item = asset_template
|
||||
| replace('{filename}', asset.filename | default('Unknown'))
|
||||
| replace('{description}', asset.description | default(''))
|
||||
@@ -1183,7 +1273,12 @@ action:
|
||||
| replace('{people}', (asset.people | default([])) | join(', '))
|
||||
| replace('{album_name}', current_album_name)
|
||||
| replace('{is_favorite}', is_favorite)
|
||||
| replace('{rating}', rating) %}
|
||||
| replace('{rating}', rating)
|
||||
| replace('{location}', formatted_location)
|
||||
| replace('{location_if_unique}', formatted_location)
|
||||
| replace('{city}', city)
|
||||
| replace('{state}', state)
|
||||
| replace('{country}', country) %}
|
||||
{% set ns.items = ns.items ~ item %}
|
||||
{% endfor %}
|
||||
{{ ns.items }}
|
||||
@@ -1348,11 +1443,16 @@ action:
|
||||
{% set ns = namespace(items = '') %}
|
||||
{% for asset in all_fetched_assets %}
|
||||
{% set asset_template = message_asset_video_template if asset.type == 'VIDEO' else message_asset_image_template %}
|
||||
{% set raw_date = asset.created_at | default('') %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date | length > 0 else none %}
|
||||
{% set raw_date = asset.created_at | default('', true) %}
|
||||
{% set dt = raw_date | as_datetime(none) if raw_date is string and raw_date | length > 0 else none %}
|
||||
{% set formatted_date = dt.strftime(date_format) if dt else '' %}
|
||||
{% set is_favorite = favorite_indicator_template if asset.is_favorite | default(false) else '' %}
|
||||
{% set rating = asset.rating | default('') | string if asset.rating not in [none, ''] else '' %}
|
||||
{% set city = asset.city | default('') %}
|
||||
{% set state = asset.state | default('') %}
|
||||
{% set country = asset.country | default('') %}
|
||||
{% set has_full_location = city | length > 0 and state | length > 0 and country | length > 0 %}
|
||||
{% set formatted_location = location_format | replace('{city}', city) | replace('{state}', state) | replace('{country}', country) if has_full_location else '' %}
|
||||
{% set item = asset_template
|
||||
| replace('{filename}', asset.filename | default('Unknown'))
|
||||
| replace('{description}', asset.description | default(''))
|
||||
@@ -1367,7 +1467,12 @@ action:
|
||||
| replace('{people}', (asset.people | default([])) | join(', '))
|
||||
| replace('{album_name}', combined_album_names)
|
||||
| replace('{is_favorite}', is_favorite)
|
||||
| replace('{rating}', rating) %}
|
||||
| replace('{rating}', rating)
|
||||
| replace('{location}', formatted_location)
|
||||
| replace('{location_if_unique}', formatted_location)
|
||||
| replace('{city}', city)
|
||||
| replace('{state}', state)
|
||||
| replace('{country}', country) %}
|
||||
{% set ns.items = ns.items ~ item %}
|
||||
{% endfor %}
|
||||
{{ ns.items }}
|
||||
@@ -1523,7 +1628,8 @@ action:
|
||||
| replace('{people}', people_list)
|
||||
| replace('{assets}', assets_list)
|
||||
| replace('{video_warning}', video_warning_text)
|
||||
| replace('{common_date}', common_date) }}
|
||||
| replace('{common_date}', common_date)
|
||||
| replace('{common_location}', common_location) }}
|
||||
|
||||
- service: notify.send_message
|
||||
target:
|
||||
@@ -1748,7 +1854,8 @@ action:
|
||||
| replace('{people}', people_list)
|
||||
| replace('{assets}', assets_list)
|
||||
| replace('{video_warning}', video_warning_text)
|
||||
| replace('{common_date}', common_date) }}
|
||||
| replace('{common_date}', common_date)
|
||||
| replace('{common_location}', common_location) }}
|
||||
|
||||
# Build URLs list for media
|
||||
# URL preference: playback_url (videos), photo_url (images) > download_url > url
|
||||
|
||||
Reference in New Issue
Block a user