Skip to content

tags: - developer - classes description: Core class descriptions for the core classes of Supernotify for Home Assistant


Core Classes

Info

See the Class Diagram for how these relate to each other.

custom_components.supernotify.transport.Transport

Base class for delivery transports.

Sub classes integrste with Home Assistant notification services or alternative notification mechanisms.

METHOD DESCRIPTION
build_standard_deliveries

Build every 'standard' (auto-generatable) delivery this transport contributes,

deliver

Delivery implementation

initialize

Async post-construction initialization

is_viable

Whether this transport currently has what it needs to auto-configure a delivery.

log_delivery_failure

Log a delivery failure, passing the exception caught in the caller's except block.

log_delivery_recovered

Call on a successful delivery - logs once if this transport was previously

simplify

Simplify text for delivery transports with speaking or plain text interfaces.

validate_action

Override in subclass if transport has fixed action or doesn't require one

ATTRIBUTE DESCRIPTION
inclusion_mode

The inclusion an auto-configured delivery for this transport should use.

TYPE: list[str]

target_categories

The target categories this transport understands, independent of any delivery.

TYPE: list[str | TargetEntityCategory]

inclusion_mode property

The inclusion an auto-configured delivery for this transport should use.

Explicit-only by default: most transports need a chat_id/channel/device_id the notification author must supply, have targets too opaque or ambiguous to map to a recipient/entity, or a channel too intrusive to fire on every notification. Override to return [INCLUSION_DEFAULT] for the few transports that can reasonably fire on every notification out of the box (e.g. email, mobile_push).

Pulled out as a separate property so can be reported in the Transport Configuration section of the Developer documentation

target_categories property

The target categories this transport understands, independent of any delivery.

A plain string names a category directly (e.g. ATTR_EMAIL); an TargetEntityCategory declares that the entity_id category is accepted, but only for entities matching its domain/platform constraints. Empty by default - a transport that doesn't declare anything here relies entirely on Delivery.select_targets()'s other qualification paths (its own name, its transport's name, or a delivery's own OPTION_TARGET_CATEGORIES override), which is the deliberate design for generic, a bring-your-own-categories transport. Queried via Delivery.target_categories, not directly - a Transport never needs to know about delivery-level config, only the reverse.

build_standard_deliveries(hass_api)

Build every 'standard' (auto-generatable) delivery this transport contributes, keyed by name: its own default (keyed by self.name) plus any extras.

Only ever called once is_viable() has returned True for the same hass_api - callers must check that first. Most overrides trust this and skip re-checking their own viability condition; the exception is a transport whose viability can only be discovered by doing the very lookup this method needs anyway (see is_viable()'s docstring) - those keep their own guard and still return an empty dict, simply because there's nothing to gain by trusting the caller there.

deliver(envelope, debug_trace=None) abstractmethod async

Delivery implementation


envelope (Envelope): envelope to be delivered
debug_trace (DebugTrace): debug info collector

initialize() async

Async post-construction initialization

is_viable(hass_api)

Whether this transport currently has what it needs to auto-configure a delivery.

Default implementation just defers to build_standard_deliveries() and checks for a non-empty result - correct for any transport, but builds (and discards) the DeliveryConfigs to answer what's otherwise a yes/no question. Override with a standalone check (matching build_standard_deliveries()'s own condition) in a transport where that's cheap and doesn't require mutating self.delivery_defaults to find out - most transports that gate purely on hass_api state (a config entry, a registered service, discovered entities) can. Skip the override where viability can only be discovered by doing the same service/entity lookup build_standard_deliveries() itself needs to build the config (e.g. discord, pushover, sms - discovering which service is available - or email, which also decides how to send based on what's found).

log_delivery_failure(err, message, *args)

Log a delivery failure, passing the exception caught in the caller's except block.

Logged at ERROR (with traceback) the first time this transport becomes unavailable, then downgraded to DEBUG for consecutive failures until it recovers - avoids spamming the log every notification while an external service/device stays down. Call alongside record_error(), which keeps tracking the lifetime error count regardless of log level.

log_delivery_recovered()

Call on a successful delivery - logs once if this transport was previously flagged unavailable, then clears the flag.

simplify(text, strip_urls=False)

Simplify text for delivery transports with speaking or plain text interfaces.

Spoken transports can be handed SSML, which the voice assistant parses itself. Simplification removes angle brackets, so applying it to SSML turns the markup into words the assistant reads out loud. When a spoken transport is given SSML, the tags are left alone and only the text around them is simplified, so emoji, URLs and symbols are still cleaned up.

validate_action(action)

Override in subclass if transport has fixed action or doesn't require one

custom_components.supernotify.notification.Notification

Bases: ArchivableObject


              flowchart TD
              custom_components.supernotify.notification.Notification[Notification]
              custom_components.supernotify.archive.ArchivableObject[ArchivableObject]

                              custom_components.supernotify.archive.ArchivableObject --> custom_components.supernotify.notification.Notification
                


              click custom_components.supernotify.notification.Notification href "" "custom_components.supernotify.notification.Notification"
              click custom_components.supernotify.archive.ArchivableObject href "" "custom_components.supernotify.archive.ArchivableObject"
            
METHOD DESCRIPTION
apply_enabled_scenarios

Set media and action_groups from scenario if defined, first come first applied

base_filename

ArchiveableObject implementation

contents

ArchiveableObject implementation

convert_notify_entities

Short circuit supernotify notify entities so they're handled directly so not

diagnostics_selected

A notification sent with debug: true asked for its trace, so it is archived

initialize

Async post-construction initialization

media_requirements

If no media defined, look for iOS / Android actions that have media defined

record_result

Debugging (and unit test) support for notifications that failed or were skipped

apply_enabled_scenarios()

Set media and action_groups from scenario if defined, first come first applied

base_filename()

ArchiveableObject implementation

contents(diagnostics=False, **_kwargs)

ArchiveableObject implementation

convert_notify_entities(target=None)

Short circuit supernotify notify entities so they're handled directly so not going round in circles via calls to notify.send_message. A genuine other-integration notify entity is left alone for NotifyEntityTransport to handle normally.

Defined here rather than in models/Target since requires access to registries.

supernotify.notify's target: field also accepts the dict shape Home Assistant's own target selector produces, e.g. {"entity_id": ["person.jey", "notify.recipient_alice"]} - unlike this integration's other dict-shaped targets (recipient/delivery config), that entity_id list is Home Assistant's raw picker output, not pre-sorted by category: a person entity picked that way still needs to end up as a person_id, same as if it had been typed directly into notify.supernotify's flat target list, or it would otherwise be silently dropped by Target() (whose entity_id category explicitly excludes the person domain - see Target.is_entity_id/is_person_id in model.py).

diagnostics_selected(outcome_policy)

A notification sent with debug: true asked for its trace, so it is archived with the full diagnostic content whatever the configured diagnostics outcomes

initialize() async

Async post-construction initialization

media_requirements(data)

If no media defined, look for iOS / Android actions that have media defined

Example is the Frigate blueprint, which generates image, video etc in the data section, that can also be used for email attachments

record_result(delivery, envelope=None, targets=None, suppression_reason=None)

Debugging (and unit test) support for notifications that failed or were skipped

custom_components.supernotify.envelope.Envelope

Bases: DupeCheckable


              flowchart TD
              custom_components.supernotify.envelope.Envelope[Envelope]
              custom_components.supernotify.common.DupeCheckable[DupeCheckable]

                              custom_components.supernotify.common.DupeCheckable --> custom_components.supernotify.envelope.Envelope
                


              click custom_components.supernotify.envelope.Envelope href "" "custom_components.supernotify.envelope.Envelope"
              click custom_components.supernotify.common.DupeCheckable href "" "custom_components.supernotify.common.DupeCheckable"
            

Wrap a notification with a specific set of targets and service data possibly customized for those targets

METHOD DESCRIPTION
__eq__

Specialized equality check for subset of attributes

__repr__

Return a concise string representation of the Envelope.

core_action_data

Build the core set of service_data dict to pass to underlying notify service

customize_data

Return data filtered by delivery data_keys_select option, pruning empty maps by default.

grab_image

Grab an image from a camera, snapshot URL, MQTT Image etc

hash

Alpha hash to reduce noise from messages with timestamps or incrementing counts

record_recipient_notifications

Update every involved recipient's notify.recipient_ entity (if it has one),

__eq__(other)

Specialized equality check for subset of attributes

__repr__()

Return a concise string representation of the Envelope.

The returned string includes the envelope's message, title, and delivery name in the form: Envelope(message={message},title={title},delivery={delivery_name}).

Primarily intended for debugging and logging; note that attribute values are inserted directly and may not be quoted or escaped.

core_action_data(force_message=True)

Build the core set of service_data dict to pass to underlying notify service

customize_data(data, prune_empty=True)

Return data filtered by delivery data_keys_select option, pruning empty maps by default.

grab_image() async

Grab an image from a camera, snapshot URL, MQTT Image etc

hash()

Alpha hash to reduce noise from messages with timestamps or incrementing counts

record_recipient_notifications(recorded_person_ids)

Update every involved recipient's notify.recipient_ entity (if it has one), so its state (or, on HA < 2026.3, its last_notified attribute - see RecipientNotifyEntity.record_notification()) reflects delivery regardless of which target form the caller used - a plain person_id, an email/phone/mobile override, notify.recipient_ itself, or anything else Recipient.initialize() folds into the same Target. Delivery target selection keeps person_ids, and generate_targets() narrows them to the recipients each envelope actually reaches (see _attach_person_ids()), so that's the one reliable link back from an arbitrary envelope to the Recipient objects it reached - see RecipientNotifyEntity.record_notification() for why this call is needed at all rather than leaving it to HA's own NotifyEntity state tracking.

recorded_person_ids are the recipients already recorded by the notification's other envelopes, which this adds to, so a recipient reached by several deliveries is only recorded once - each is a state write, and would otherwise show up in the logbook as several identical entries at the same moment.

custom_components.supernotify.scenario.Scenario

METHOD DESCRIPTION
attributes

Return scenario attributes

contents

Archive friendly view of scenario

evaluate

Evaluate scenario conditions

trace

Trace scenario condition execution

validate

Validate Home Assistant conditiion definition at initiation

ATTRIBUTE DESCRIPTION
is_manual

A scenario with no conditions, which only applies when its manual state is on

TYPE: bool

is_manual property

A scenario with no conditions, which only applies when its manual state is on

attributes(include_condition=True, include_trace=False)

Return scenario attributes

contents(minimal=False, **_kwargs)

Archive friendly view of scenario

evaluate(condition_variables)

Evaluate scenario conditions

trace(condition_variables) async

Trace scenario condition execution

validate(valid_action_group_names=None) async

Validate Home Assistant conditiion definition at initiation

custom_components.supernotify.people.Recipient

Recipient to distinguish from the native HA Person.

The "future native entity use" this class was once staged for (BinarySensorDeviceClass, EntityCategory, etc.) has arrived as SupernotifyRecipientBinarySensor in binary_sensor.py - a wrapper Entity holding a reference to a Recipient, the same composition already used for RecipientNotifyEntity above, rather than this plain domain object inheriting from Entity.

METHOD DESCRIPTION
attributes

For exposure as entity state

disabling_delivery_names

Explicitly overriding enabled state

enabling_delivery_names

Explicitly overriding enabled state

attributes()

For exposure as entity state

disabling_delivery_names()

Explicitly overriding enabled state

enabling_delivery_names()

Explicitly overriding enabled state

custom_components.supernotify.target.Target

METHOD DESCRIPTION
__add__

Create a new target by adding another to this one

__eq__

Compare two targets

__len__

How many targets, whether direct or indirect

__sub__

Create a new target by removing another from this one, ignoring target_data

select

Narrow this target to what a delivery can use, leaving this one untouched

__add__(other)

Create a new target by adding another to this one

__eq__(other)

Compare two targets

__len__()

How many targets, whether direct or indirect

__sub__(other)

Create a new target by removing another from this one, ignoring target_data

select(categories, own_names, hass_api, target_selector=None)

Narrow this target to what a delivery can use, leaving this one untouched

categories are the delivery's declared target categories, and own_names its own name and its transport's. A target category named after either is always destined for that delivery. The two serve different purposes and both stay available: - the TRANSPORT name (sms:value) reaches every delivery of that transport, so scenario/time/occupancy selection logic can still decide which one actually fires - the same as it would for a plain, auto-matched value - a specific DELIVERY name (shortcode_sms:value) pins the target to just that one delivery, for when two deliveries of the same transport must stay distinct (e.g. email vs html_email)

person_ids are always kept, whatever the delivery declares, since they aren't delivered to but are the link back to the recipients a delivery reaches (see Notification.generate_targets(), which narrows them to those actually in each envelope). They are kept out of the target_selector too, as it's for choosing between values a transport can address.

custom_components.supernotify.model.DeliveryConfig

Shared config for transport defaults and Delivery definitions

METHOD DESCRIPTION
__repr__

Log friendly representation

__repr__()

Log friendly representation

custom_components.supernotify.model.TransportConfig

custom_components.supernotify.snoozer.Snooze

METHOD DESCRIPTION
__eq__

Check if two snoozes for the same thing

__repr__

Return a string representation of the object.

to_storage_dict

Full-fidelity serialization for persistence (unlike export(), which is a display

__eq__(other)

Check if two snoozes for the same thing

__repr__()

Return a string representation of the object.

to_storage_dict()

Full-fidelity serialization for persistence (unlike export(), which is a display summary that loses the date part of timestamps).

custom_components.supernotify.model.ConditionVariables dataclass

Variables presented to all condition evaluations

Attributes

applied_scenarios (list[str]): Scenarios that have been applied
required_scenarios (list[str]): Scenarios that must be applied
constrain_scenarios (list[str]): Only scenarios in this list, or in explicit apply_scenarios, can be applied
notification_priority (str): Priority of the notification
notification_message (str): Message of the notification
notification_title (str): Title of the notification
occupancy (list[str]): List of occupancy scenarios
notification_data (dict[str,Any]): Additional data passed on notify action call

custom_components.supernotify.people.PeopleRegistry

METHOD DESCRIPTION
async_refresh_entity

Re-publish one recipient's binary_sensor now

mobile_devices_for_person

Auto detect mobile_app targets for a person.

recipient_entities

Every registered recipient binary_sensor - used by supernotify.refresh_entities.

register_entity

Called by SupernotifyRecipientBinarySensor.async_added_to_hass().

unregister_entity

Called by SupernotifyRecipientBinarySensor.async_will_remove_from_hass().

async_refresh_entity(name)

Re-publish one recipient's binary_sensor now

mobile_devices_for_person(person_entity_id)

Auto detect mobile_app targets for a person.

Targets not currently validated as async registration may not be complete at this stage


person_entity_id (str): _description_

list: mobile target actions for this person

recipient_entities()

Every registered recipient binary_sensor - used by supernotify.refresh_entities.

register_entity(name, entity)

Called by SupernotifyRecipientBinarySensor.async_added_to_hass().

unregister_entity(name)

Called by SupernotifyRecipientBinarySensor.async_will_remove_from_hass().

custom_components.supernotify.delivery.DeliveryRegistry

METHOD DESCRIPTION
async_refresh_entity

Re-publish one delivery or transport binary_sensor now, by its unique_id

initialize_transport_deliveries

Validate and initialize deliveries at startup for this transport

legacy_entities

Every registered delivery and transport binary_sensor - used by supernotify.refresh_entities.

register_entity

Called by a delivery or transport binary_sensor's async_added_to_hass().

resolve_name

Backward compatibility for the original 'DEFAULT_x' auto-configured naming,

unload_unused_transports

Drop any transport that ended up with no delivery at all - explicit or auto-generated.

unregister_entity

Called by a delivery or transport binary_sensor's async_will_remove_from_hass().

ATTRIBUTE DESCRIPTION
implicit_deliveries

Deliveries switched on all the time via implicit inclusion

TYPE: list[Delivery]

implicit_deliveries property

Deliveries switched on all the time via implicit inclusion

async_refresh_entity(unique_id)

Re-publish one delivery or transport binary_sensor now, by its unique_id

initialize_transport_deliveries(context, transport) async

Validate and initialize deliveries at startup for this transport

legacy_entities()

Every registered delivery and transport binary_sensor - used by supernotify.refresh_entities.

register_entity(unique_id, entity)

Called by a delivery or transport binary_sensor's async_added_to_hass().

resolve_name(name)

Backward compatibility for the original 'DEFAULT_x' auto-configured naming, long since replaced by plain transport names: a reference to the old 'DEFAULT_x' form resolves to the current 'x' delivery, if that's what actually exists now.

unload_unused_transports()

Drop any transport that ended up with no delivery at all - explicit or auto-generated.

Deliberately deferred until both initialize_transport_deliveries() (explicit) and build_standard_deliveries() (implicit) have run, rather than decided per-transport up front: whether a transport is worth having can only be known once the full, resolved set of deliveries exists - for a transport like generic (bring-your-own-action, entirely delivery-driven), there's no transport-level state to check in advance at all.

unregister_entity(unique_id)

Called by a delivery or transport binary_sensor's async_will_remove_from_hass().

custom_components.supernotify.scenario.ScenarioRegistry

METHOD DESCRIPTION
async_refresh_entity

Re-publish one scenario's binary_sensor now, whether or not periodic refresh is on

async_refresh_scenario_states

Ask each affected scenario's binary_sensor entity to re-read and re-publish its state.

register_entity

Called by SupernotifyScenarioBinarySensor.async_added_to_hass().

scenario_has_state

Whether a scenario has any state to report - anything that hasn't opted out with

scenario_is_on

is_on for SupernotifyScenarioBinarySensor - None maps to STATE_UNKNOWN.

unregister_entity

Called by SupernotifyScenarioBinarySensor.async_will_remove_from_hass().

async_refresh_entity(name)

Re-publish one scenario's binary_sensor now, whether or not periodic refresh is on

async_refresh_scenario_states(*args)

Ask each affected scenario's binary_sensor entity to re-read and re-publish its state.

Triggered by the 1-minute timer (time/date scenarios and any dependency not captured by entity extraction) and by state changes of the scenarios' condition entities (immediate reactivity). The entity's own is_on property (via scenario_is_on() above) does the actual (pure, in-memory) evaluation on read; this only decides which entities need to refresh, and is a no-op for a scenario with no entity registered yet (e.g. before the binary_sensor platform has finished loading).

register_entity(name, entity)

Called by SupernotifyScenarioBinarySensor.async_added_to_hass().

scenario_has_state(scenario)

Whether a scenario has any state to report - anything that hasn't opted out with expose_state. That is the evaluated state of its conditions if it has any, otherwise a manual state that something outside Supernotify sets, see Scenario.manual_active.

scenario_is_on(scenario)

is_on for SupernotifyScenarioBinarySensor - None maps to STATE_UNKNOWN.

unregister_entity(name)

Called by SupernotifyScenarioBinarySensor.async_will_remove_from_hass().