Skip to content

v0.4.2

Choose a tag to compare

@Geek-MD Geek-MD released this 25 Feb 03:04
· 431 commits to main since this release
6f1e3cb

v0.4.2 - 2026-02-23

Added

  • integration_type: "hub" (manifest.json): Marks the integration as a hub so Home
    Assistant displays it like the MQTT integration — with a CONFIGURE button and an
    ADD DEVICE button on the integration card.
  • single_config_entry: true (manifest.json): Only one Concierge Services instance
    (one monitored email account) is allowed at a time.
  • Options Flow (config_flow.py → OptionsFlowHandler): The CONFIGURE button
    opens a pre-filled form to reconfigure the IMAP credentials and friendly name without
    deleting and re-adding the integration.
  • Subentry Flow (config_flow.py → ServiceSubentryFlowHandler): The ADD DEVICE
    button scans the inbox, filters out services already added, and lets the user select one
    new service account to add as a device. Each device also supports a reconfigure step
    so the service name can be updated via the UI.
  • New constants (const.py): CONF_SERVICE_ID, CONF_SERVICE_NAME,
    CONF_SERVICE_TYPE, CONF_SAMPLE_FROM, CONF_SAMPLE_SUBJECT — used as keys in
    subentry data instead of the old flat services_metadata dict.
  • Subentry strings (strings.json, translations/en.json, translations/es.json):
    New config_subentries.service section with step, error, and abort messages for the
    add-device and reconfigure flows.

Changed

  • Initial config flow (config_flow.py): Simplified to two steps only — IMAP
    credentials (user) and friendly name (finalize). Service detection during initial
    setup has been removed; services are added individually after setup via ADD DEVICE.
  • sensor.py: async_setup_entry now iterates over config_entry.subentries to
    create service sensors, instead of reading from the old services / services_metadata
    keys in config_entry.data. The coordinator reads effective credentials from
    {**entry.data, **entry.options} so options-flow changes are respected without
    needing to reconfigure from scratch.
  • __init__.py: Removed explicit device-registry calls (devices are created via
    DeviceInfo in the sensor entities). Added an add_update_listener so the entry
    reloads automatically when options or subentries change.
  • manifest.json: Version bumped to 0.4.2.

Removed

  • CONF_SERVICES constant from const.py (replaced by individual subentries).
  • area_id support from the initial config flow (areas can still be assigned from the
    device card after setup).
  • detect_services and select_services steps from the initial config flow.

Migration note

Existing config entries from v0.4.0/v0.4.1 will continue to load (the connection sensor
works without subentries). Previously configured service sensors will not appear
automatically — use the ADD DEVICE button to re-add them as subentries.

Added

  • Service-Type Constants (const.py): Added SERVICE_TYPE_WATER, SERVICE_TYPE_GAS,
    SERVICE_TYPE_ELECTRICITY, SERVICE_TYPE_TELECOM, and SERVICE_TYPE_UNKNOWN constants
    to classify detected services.

  • Modular Type-Specific Extractors (attribute_extractor.py): Extraction tools are
    now organised by service type so each utility category can use patterns tuned to its
    own email format:

    Service type Extra attributes extracted
    water address + customer_number (packed-values), consumption_m3, meter_reading, meter_number
    gas total_amount (plain-number override), metropuntos, consumption_m3 (label-based)
    electricity folio, boleta_date, address (from ubicado en), consumption_kwh, consumption_type, next_billing_period_start/end
  • due_date extraction (attribute_extractor.py): New generic extractor
    (_extract_due_date) searches for Fecha de vencimiento label — confirmed in
    both Metrogas and Enel emails.

  • _extract_type_specific_attributes routing helper (attribute_extractor.py):
    Dispatches to the correct type-specific extractor based on service_type.

  • classify_service_type utility (service_detector.py): Public function that
    infers the service type from the email From address and Subject line.

Changed

  • _strip_html (attribute_extractor.py): Now applies html.unescape() a second
    time after the HTML parser so that double-encoded entities (ó →
    ó → ó) found in Aguas Andinas emails are fully decoded.

  • _CUSTOMER_LABELS (attribute_extractor.py): Made de optional so both
    Número de Cliente: and Número Cliente: (Metrogas) are matched.

  • _extract_from_subject folio patterns (attribute_extractor.py): Added
    r"nro\.?\s+([0-9]{6,})" for the Metrogas Boleta Metrogas Nro. NNNNNN format.

  • SERVICE_PATTERNS (service_detector.py): 3-tuples with service type added.

  • DetectedService dataclass (service_detector.py): Added service_type field.

  • ConciergeServiceSensor.extra_state_attributes (sensor.py): Exposes
    service_type; also filters None attribute values so fields cleared by
    type-specific extractors are omitted from the HA UI rather than shown as null.

  • Config flow (config_flow.py): Stores service_type in services_metadata.

Water extractor — Aguas Andinas (reference email: February 2026)

The Aguas Andinas HTML-only email uses a two-column table layout: labels
(Dirección:, Número de Cuenta:, Período de Facturación:) are in the left <td>;
all values are packed in the right <td> as a single paragraph:
ADDRESS ACCOUNT_NUM DATE al DATE.

Root cause Fix
&amp;oacute; double-encoded HTML entities _strip_html applies html.unescape() twice
Labels and values in separate <td> — generic label extractor gives wrong results _WATER_AA_PACKED_RE detects the ALL-CAPS address + \d{5,}-\d account pattern; results override generic values via update()

Fields now correctly extracted: billing_period_start/end ✓ total_amount ✓
address ✓ (was 'Número de Cuenta:') customer_number ✓ (was street number '385-515')

Gas extractor — Metrogas (reference email: January 2026)

  • Folio from subject Nro. pattern, plain-number total, metropuntos loyalty points.
  • Gas consumption (m³) is not in the email body — only in the PDF attachment.

Electricity extractor — Enel Distribución Chile (reference email: February 2026)

The Enel email has both text/plain and text/html parts; extractor uses plain text.

What we learned How it's handled
Invoice number in body: N° Boleta 361692435 del 02-02-2026 _ELEC_ENEL_FOLIO_RE + _ELEC_ENEL_BOLETA_DATE_RE → folio + boleta_date
Address follows ubicado en (not Dirección:) _ELEC_ENEL_ADDRESS_RE → address
No current billing period in email; first two dates are boleta date + due date (WRONG) Electricity extractor sets billing_period_start/end = None; sensor filters None values
Próximo periodo de facturación = NEXT billing period _ELEC_ENEL_NEXT_PERIOD_RE → next_billing_period_start/end
Consumo real / Consumo estimado quality flag _ELEC_ENEL_CONSUMPTION_TYPE_RE → consumption_type
505 kWh in email body bare-kWh fallback → consumption_kwh