ha-mqtt-publisher


Nameha-mqtt-publisher JSON
Version 0.3.2 PyPI version JSON
download
home_pageNone
SummaryAn MQTT publisher package
upload_time2025-08-11 18:31:16
maintainerNone
docs_urlNone
authorronschaeffer
requires_python>=3.11
licenseMIT
keywords mqtt publisher iot
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            HA MQTT Publisher

[![PyPI](https://img.shields.io/pypi/v/ha-mqtt-publisher.svg)](https://pypi.org/project/ha-mqtt-publisher/)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Code style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

A Python MQTT publishing library with Home Assistant MQTT Discovery support.

## Features

- MQTT publish support using paho-mqtt 2.x (username/password, TLS, client_id, keepalive, Last Will)
- MQTT protocol selection (3.1, 3.1.1, 5.0)
- Default QoS and retain settings per configuration
- Configuration via YAML with environment variable substitution
- Home Assistant Discovery helpers: Device/Entity classes, Status sensor, DiscoveryManager
- One-time discovery publication with state tracking
- Validation of HA fields with optional extension lists
- Configurable logging levels for connection, publish, and discovery

## Installation

- Requires Python 3.11+
- pip: pip install ha-mqtt-publisher

## Configuration

Provide a YAML configuration and use environment variables for sensitive values. The library reads nested keys like mqtt.* and home_assistant.*.

Example config.yaml

```yaml
mqtt:
	broker_url: "${MQTT_BROKER_URL}"
	broker_port: "${MQTT_BROKER_PORT}"
	client_id: "${MQTT_CLIENT_ID}"
	security: "${MQTT_SECURITY}"           # none | username | tls | tls_with_client_cert
	auth:
		username: "${MQTT_USERNAME}"
		password: "${MQTT_PASSWORD}"
	tls:
		verify: "${MQTT_TLS_VERIFY}"         # true | false
		ca_cert: "${MQTT_TLS_CA_CERT}"
		client_cert: "${MQTT_TLS_CLIENT_CERT}"
		client_key: "${MQTT_TLS_CLIENT_KEY}"
	max_retries: "${MQTT_MAX_RETRIES}"
	default_qos: "${MQTT_DEFAULT_QOS}"
	default_retain: "${MQTT_DEFAULT_RETAIN}"

home_assistant:
	discovery_prefix: "${HA_DISCOVERY_PREFIX}"      # default: homeassistant
	strict_validation: "${HA_STRICT_VALIDATION}"     # true | false (default true)
	discovery_state_file: "${HA_DISCOVERY_STATE_FILE}"
	extra_allowed: {}  # optional extension lists (entity categories, etc.)

	# Optional self-heal verification (one-time mode)
	ensure_discovery_on_startup: "${HA_ENSURE_DISCOVERY_ON_STARTUP}"  # true | false (default false)
	ensure_discovery_timeout: "${HA_ENSURE_DISCOVERY_TIMEOUT}"        # seconds (default 2.0)

	# Optional device-bundle behavior for modern HA
	bundle_only_mode: "${HA_BUNDLE_ONLY_MODE}"        # true | false (default false)

app:
	# Optional metadata used for bundle origin info (o)
	name: "${APP_NAME}"
	sw_version: "${APP_SW_VERSION}"
	configuration_url: "${APP_CONFIGURATION_URL}"
```

Notes
- Use ${VAR} placeholders and set environment variables for your runtime.
- mqtt.* is used by the MQTTPublisher. home_assistant.* is used by discovery helpers.
- app.* is optional and only used to populate origin metadata in bundled device configs.

### Quick reference: configuration keys

Home Assistant (home_assistant.*)

| Key | Type | Default | Purpose |
|-----|------|---------|---------|
| discovery_prefix | string | homeassistant | Base discovery topic prefix |
| strict_validation | bool | true | Validate entity fields against known enums |
| discovery_state_file | string | — | JSON file path for one-time mode state |
| extra_allowed | dict | {} | Extend allowed values (entity categories, etc.) |
| ensure_discovery_on_startup | bool | false | Verify retained discovery topics and republish missing ones before publishing (one-time mode) |
| ensure_discovery_timeout | float | 2.0 | Wait time for retained discovery messages |
| bundle_only_mode | bool | false | For modern HA: verify/publish only the device bundle topic |

Application metadata (app.*) used in bundled device origin block (optional)

| Key | Type | Purpose |
|-----|------|---------|
| name | string | App name for origin (o.name) |
| sw_version | string | App/software version (o.sw) |
| configuration_url | string | URL to docs/config (o.url) |

## Usage

### Publish messages with MQTTPublisher

```python
from ha_mqtt_publisher.config import MQTTConfig
from ha_mqtt_publisher.publisher import MQTTPublisher

# Build a config dict (could also load from YAML and call MQTTConfig.from_dict)
mqtt_cfg = MQTTConfig.build_config(
		broker_url="${MQTT_BROKER_URL}",
		broker_port="${MQTT_BROKER_PORT}",
		client_id="${MQTT_CLIENT_ID}",
		security="${MQTT_SECURITY}",
		username="${MQTT_USERNAME}",
		password="${MQTT_PASSWORD}",
		tls={"verify": True} if "${MQTT_SECURITY}" in ("tls", "tls_with_client_cert") else None,
		default_qos=1,
		default_retain=True,
)

publisher = MQTTPublisher(config=mqtt_cfg)
publisher.connect()

publisher.publish(
		topic="demo/hello",
		payload="{\"msg\": \"hello\"}",
		qos=1,
		retain=True,
)

publisher.disconnect()
```

### Home Assistant Discovery

Declare a device and entities, then publish discovery configs. Use one-time mode to avoid re-publishing.

```python
from ha_mqtt_publisher.config import Config
from ha_mqtt_publisher.publisher import MQTTPublisher
from ha_mqtt_publisher.ha_discovery import Device, Sensor
from ha_mqtt_publisher.ha_discovery import publish_discovery_configs, create_status_sensor

# Load full application config for discovery (reads mqtt.* and home_assistant.*)
app_config = Config("config.yaml")

# MQTT client using the same YAML (mqtt.* section)
publisher = MQTTPublisher(config={
		"broker_url": app_config.get("mqtt.broker_url"),
		"broker_port": app_config.get("mqtt.broker_port", 1883),
		"client_id": app_config.get("mqtt.client_id", "ha-mqtt-pub"),
		"security": app_config.get("mqtt.security", "none"),
		"auth": app_config.get("mqtt.auth"),
		"tls": app_config.get("mqtt.tls"),
		"default_qos": app_config.get("mqtt.default_qos", 1),
		"default_retain": app_config.get("mqtt.default_retain", True),
})
publisher.connect()

device = Device(app_config)
temp = Sensor(
		config=app_config,
		device=device,
		name="Room Temperature",
		unique_id="room_temp_1",
		state_topic="home/room/temperature",
		unit_of_measurement="°C",
)

status = create_status_sensor(app_config, device)

publish_discovery_configs(
		config=app_config,
		publisher=publisher,
		entities=[temp, status],
		device=device,
		one_time_mode=True,
)

# After discovery, publish state values
publisher.publish("home/room/temperature", "23.4", qos=1, retain=True)
```

### Discovery modes: entity-centric and device-centric

- Entity-centric (default): Publish per-entity config to <prefix>/<component>/.../config. Each payload includes a device block for grouping.
- Device-centric (optional): Publish one device config to <prefix>/device/<device_id>/config, then publish entities as needed.
	- Optionally, publish a single bundled message that includes all entities. You can also request the bundle be emitted before per-entity topics via emit_device_bundle=True in publish_discovery_configs.

#### Which mode should I use?

- Use entity-centric when you need maximum backward compatibility with all HA versions or want explicit per-entity config topics.
- Use device bundle when your HA supports the bundled device config for faster provisioning, single-topic idempotency, and cleaner device metadata. You can still publish per-entity topics alongside the bundle by default.

Key differences
- Topic shape: entity-centric uses component topics per entity; bundle uses one device topic plus runtime state topics.
- Device block: per-entity configs repeat device metadata; bundle has a single dev block.
- Keys inside bundle: entities are keyed by unique_id; entity-centric uses object_id in topic paths.
- Transport defaults: bundle may include qos/retain as top-level hints; per-entity uses transport options only.

Device-centric publish example

```python
from ha_mqtt_publisher.ha_discovery import Device, publish_device_config

device = Device(app_config)

# Choose topic device_id explicitly, or omit to use the first identifier
ok = publish_device_config(
	config=app_config,
	publisher=publisher,
	device=device,
	device_id="living_room_bridge",
)
```

Bundled device-centric publish (single message)

```python
from ha_mqtt_publisher.ha_discovery import Device, Sensor, publish_device_bundle

device = Device(app_config)
temp = Sensor(app_config, device, name="Temperature", unique_id="temp", state_topic="room/t")
humid = Sensor(app_config, device, name="Humidity", unique_id="humid", state_topic="room/h")

# Publishes one config message containing device (dev) and components (cmps)
publish_device_bundle(
	config=app_config,
	publisher=publisher,
	device=device,
	entities=[temp, humid],
)
```

### One-time publication

- Enabled by passing one_time_mode=True to publish_discovery_configs.
- Tracks published topics in home_assistant.discovery_state_file.

## Supported Home Assistant components

### About "Device" (registry grouping)

- Device is metadata included in each entity's discovery payload; it is not a standalone component or topic.
- Home Assistant uses it to group entities in the Device Registry and display manufacturer/model, versions, and links.
- Create one Device per physical/logical device and pass it to all related entities; removal happens when all related entities are removed.

### Components

| Type            | Component key       | Notes |
|-----------------|---------------------|-------|
| Sensor          | sensor              | state_topic required |
| Binary Sensor   | binary_sensor       | state_topic required; device_class supported |
| Switch          | switch              | command/state topics supported |
| Light           | light               | payload_on/off defaults; command/state |
| Cover           | cover               | payload_open/close/stop defaults |
| Climate         | climate             | topic fields per HA spec |
| Fan             | fan                 | payload_on/off defaults |
| Lock            | lock                | payload_lock/unlock defaults |
| Number          | number              | numeric set/get |
| Select          | select              | options via extra attributes |
| Text            | text                | text set/get |
| Button          | button              | stateless trigger |
| Device Tracker  | device_tracker      | presence/location topics |
| Alarm Control   | alarm_control_panel | arm/disarm topics as applicable |
| Camera          | camera              | image/stream topics as applicable |
| Status Sensor   | sensor (helper)     | convenience entity for app status |

Notes
- Validation covers entity_category, availability_mode, sensor state_class, and device_class.
- Additional allowed values can be provided via home_assistant.extra_allowed.

## Testing

```bash
pytest -q
```

Entity-centric verification snippet

```python
from ha_mqtt_publisher.ha_discovery import ensure_discovery

# Verify per-entity discovery topics; republish any missing
ensure_discovery(
	config=app_config,
	publisher=publisher,
	entities=[temp, humid, status],
	device=device,
	one_time_mode=True,
)
```
See also: examples/entity_verification.py

Emit device bundle within publish_discovery_configs

```python
publish_discovery_configs(
	config=app_config,
	publisher=publisher,
	entities=[temp, humid],
	device=device,
	one_time_mode=True,
	emit_device_bundle=True,  # bundle first, then per-entity topics
)
```

### Discovery verification (optional self-heal)

If you want the library to verify retained discovery topics exist on the broker and republish any that are missing, enable the verification pass when using one-time mode.

- Config flags:
  - home_assistant.ensure_discovery_on_startup: true|false (default false)
  - home_assistant.ensure_discovery_timeout: float seconds (default 2.0)
  - home_assistant.bundle_only_mode: true|false (default false). When true, verification checks only the device bundle topic and republishes it if missing.

Lightweight example

```python
from ha_mqtt_publisher.ha_discovery import ensure_discovery

# Before publish_discovery_configs (optional; publish_discovery_configs will call this automatically
# when one_time_mode=True and home_assistant.ensure_discovery_on_startup is true)
ensure_discovery(
	config=app_config,
	publisher=publisher,
	entities=[temp, status],
	device=device,
	timeout=app_config.get("home_assistant.ensure_discovery_timeout", 2.0),
	one_time_mode=True,
)
```

Modern HA: bundle-only verification example

If your Home Assistant version supports device bundle configs and you set:

```yaml
home_assistant:
	bundle_only_mode: true
```

You can verify (and republish if missing) just the bundle topic:

```python
from ha_mqtt_publisher.ha_discovery import ensure_discovery

# Assumes home_assistant.bundle_only_mode: true in your YAML
ensure_discovery(
		config=app_config,
		publisher=publisher,
		entities=[temp, humid],  # included in the bundle
		device=device,
		device_id="living_room_bridge",  # optional; defaults to first device identifier
		one_time_mode=True,
)
```

See also: examples/bundle_only_verification.py

## Development

- Install dev dependencies: pip install -e .[dev]
- Lint and format: ruff check . && ruff format .

## Troubleshooting

- Connection refused with TLS/non-TLS port mismatch: ensure tls settings align with broker_port (1883 non-TLS, 8883 TLS).
- Discovery not appearing: verify discovery_prefix and that MQTT messages are retained on config topics.

## License

MIT

## Contributing

Issues and pull requests are welcome in the GitHub repository.

## Support

Open a GitHub issue for questions and problems.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "ha-mqtt-publisher",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "mqtt, publisher, iot",
    "author": "ronschaeffer",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/df/8f/36f5746181daffdbafbb56f0f3c782fbd1821c655462ab2ad673362c31ec/ha_mqtt_publisher-0.3.2.tar.gz",
    "platform": null,
    "description": "HA MQTT Publisher\n\n[![PyPI](https://img.shields.io/pypi/v/ha-mqtt-publisher.svg)](https://pypi.org/project/ha-mqtt-publisher/)\n[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Code style: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n\nA Python MQTT publishing library with Home Assistant MQTT Discovery support.\n\n## Features\n\n- MQTT publish support using paho-mqtt 2.x (username/password, TLS, client_id, keepalive, Last Will)\n- MQTT protocol selection (3.1, 3.1.1, 5.0)\n- Default QoS and retain settings per configuration\n- Configuration via YAML with environment variable substitution\n- Home Assistant Discovery helpers: Device/Entity classes, Status sensor, DiscoveryManager\n- One-time discovery publication with state tracking\n- Validation of HA fields with optional extension lists\n- Configurable logging levels for connection, publish, and discovery\n\n## Installation\n\n- Requires Python 3.11+\n- pip: pip install ha-mqtt-publisher\n\n## Configuration\n\nProvide a YAML configuration and use environment variables for sensitive values. The library reads nested keys like mqtt.* and home_assistant.*.\n\nExample config.yaml\n\n```yaml\nmqtt:\n\tbroker_url: \"${MQTT_BROKER_URL}\"\n\tbroker_port: \"${MQTT_BROKER_PORT}\"\n\tclient_id: \"${MQTT_CLIENT_ID}\"\n\tsecurity: \"${MQTT_SECURITY}\"           # none | username | tls | tls_with_client_cert\n\tauth:\n\t\tusername: \"${MQTT_USERNAME}\"\n\t\tpassword: \"${MQTT_PASSWORD}\"\n\ttls:\n\t\tverify: \"${MQTT_TLS_VERIFY}\"         # true | false\n\t\tca_cert: \"${MQTT_TLS_CA_CERT}\"\n\t\tclient_cert: \"${MQTT_TLS_CLIENT_CERT}\"\n\t\tclient_key: \"${MQTT_TLS_CLIENT_KEY}\"\n\tmax_retries: \"${MQTT_MAX_RETRIES}\"\n\tdefault_qos: \"${MQTT_DEFAULT_QOS}\"\n\tdefault_retain: \"${MQTT_DEFAULT_RETAIN}\"\n\nhome_assistant:\n\tdiscovery_prefix: \"${HA_DISCOVERY_PREFIX}\"      # default: homeassistant\n\tstrict_validation: \"${HA_STRICT_VALIDATION}\"     # true | false (default true)\n\tdiscovery_state_file: \"${HA_DISCOVERY_STATE_FILE}\"\n\textra_allowed: {}  # optional extension lists (entity categories, etc.)\n\n\t# Optional self-heal verification (one-time mode)\n\tensure_discovery_on_startup: \"${HA_ENSURE_DISCOVERY_ON_STARTUP}\"  # true | false (default false)\n\tensure_discovery_timeout: \"${HA_ENSURE_DISCOVERY_TIMEOUT}\"        # seconds (default 2.0)\n\n\t# Optional device-bundle behavior for modern HA\n\tbundle_only_mode: \"${HA_BUNDLE_ONLY_MODE}\"        # true | false (default false)\n\napp:\n\t# Optional metadata used for bundle origin info (o)\n\tname: \"${APP_NAME}\"\n\tsw_version: \"${APP_SW_VERSION}\"\n\tconfiguration_url: \"${APP_CONFIGURATION_URL}\"\n```\n\nNotes\n- Use ${VAR} placeholders and set environment variables for your runtime.\n- mqtt.* is used by the MQTTPublisher. home_assistant.* is used by discovery helpers.\n- app.* is optional and only used to populate origin metadata in bundled device configs.\n\n### Quick reference: configuration keys\n\nHome Assistant (home_assistant.*)\n\n| Key | Type | Default | Purpose |\n|-----|------|---------|---------|\n| discovery_prefix | string | homeassistant | Base discovery topic prefix |\n| strict_validation | bool | true | Validate entity fields against known enums |\n| discovery_state_file | string | \u2014 | JSON file path for one-time mode state |\n| extra_allowed | dict | {} | Extend allowed values (entity categories, etc.) |\n| ensure_discovery_on_startup | bool | false | Verify retained discovery topics and republish missing ones before publishing (one-time mode) |\n| ensure_discovery_timeout | float | 2.0 | Wait time for retained discovery messages |\n| bundle_only_mode | bool | false | For modern HA: verify/publish only the device bundle topic |\n\nApplication metadata (app.*) used in bundled device origin block (optional)\n\n| Key | Type | Purpose |\n|-----|------|---------|\n| name | string | App name for origin (o.name) |\n| sw_version | string | App/software version (o.sw) |\n| configuration_url | string | URL to docs/config (o.url) |\n\n## Usage\n\n### Publish messages with MQTTPublisher\n\n```python\nfrom ha_mqtt_publisher.config import MQTTConfig\nfrom ha_mqtt_publisher.publisher import MQTTPublisher\n\n# Build a config dict (could also load from YAML and call MQTTConfig.from_dict)\nmqtt_cfg = MQTTConfig.build_config(\n\t\tbroker_url=\"${MQTT_BROKER_URL}\",\n\t\tbroker_port=\"${MQTT_BROKER_PORT}\",\n\t\tclient_id=\"${MQTT_CLIENT_ID}\",\n\t\tsecurity=\"${MQTT_SECURITY}\",\n\t\tusername=\"${MQTT_USERNAME}\",\n\t\tpassword=\"${MQTT_PASSWORD}\",\n\t\ttls={\"verify\": True} if \"${MQTT_SECURITY}\" in (\"tls\", \"tls_with_client_cert\") else None,\n\t\tdefault_qos=1,\n\t\tdefault_retain=True,\n)\n\npublisher = MQTTPublisher(config=mqtt_cfg)\npublisher.connect()\n\npublisher.publish(\n\t\ttopic=\"demo/hello\",\n\t\tpayload=\"{\\\"msg\\\": \\\"hello\\\"}\",\n\t\tqos=1,\n\t\tretain=True,\n)\n\npublisher.disconnect()\n```\n\n### Home Assistant Discovery\n\nDeclare a device and entities, then publish discovery configs. Use one-time mode to avoid re-publishing.\n\n```python\nfrom ha_mqtt_publisher.config import Config\nfrom ha_mqtt_publisher.publisher import MQTTPublisher\nfrom ha_mqtt_publisher.ha_discovery import Device, Sensor\nfrom ha_mqtt_publisher.ha_discovery import publish_discovery_configs, create_status_sensor\n\n# Load full application config for discovery (reads mqtt.* and home_assistant.*)\napp_config = Config(\"config.yaml\")\n\n# MQTT client using the same YAML (mqtt.* section)\npublisher = MQTTPublisher(config={\n\t\t\"broker_url\": app_config.get(\"mqtt.broker_url\"),\n\t\t\"broker_port\": app_config.get(\"mqtt.broker_port\", 1883),\n\t\t\"client_id\": app_config.get(\"mqtt.client_id\", \"ha-mqtt-pub\"),\n\t\t\"security\": app_config.get(\"mqtt.security\", \"none\"),\n\t\t\"auth\": app_config.get(\"mqtt.auth\"),\n\t\t\"tls\": app_config.get(\"mqtt.tls\"),\n\t\t\"default_qos\": app_config.get(\"mqtt.default_qos\", 1),\n\t\t\"default_retain\": app_config.get(\"mqtt.default_retain\", True),\n})\npublisher.connect()\n\ndevice = Device(app_config)\ntemp = Sensor(\n\t\tconfig=app_config,\n\t\tdevice=device,\n\t\tname=\"Room Temperature\",\n\t\tunique_id=\"room_temp_1\",\n\t\tstate_topic=\"home/room/temperature\",\n\t\tunit_of_measurement=\"\u00b0C\",\n)\n\nstatus = create_status_sensor(app_config, device)\n\npublish_discovery_configs(\n\t\tconfig=app_config,\n\t\tpublisher=publisher,\n\t\tentities=[temp, status],\n\t\tdevice=device,\n\t\tone_time_mode=True,\n)\n\n# After discovery, publish state values\npublisher.publish(\"home/room/temperature\", \"23.4\", qos=1, retain=True)\n```\n\n### Discovery modes: entity-centric and device-centric\n\n- Entity-centric (default): Publish per-entity config to <prefix>/<component>/.../config. Each payload includes a device block for grouping.\n- Device-centric (optional): Publish one device config to <prefix>/device/<device_id>/config, then publish entities as needed.\n\t- Optionally, publish a single bundled message that includes all entities. You can also request the bundle be emitted before per-entity topics via emit_device_bundle=True in publish_discovery_configs.\n\n#### Which mode should I use?\n\n- Use entity-centric when you need maximum backward compatibility with all HA versions or want explicit per-entity config topics.\n- Use device bundle when your HA supports the bundled device config for faster provisioning, single-topic idempotency, and cleaner device metadata. You can still publish per-entity topics alongside the bundle by default.\n\nKey differences\n- Topic shape: entity-centric uses component topics per entity; bundle uses one device topic plus runtime state topics.\n- Device block: per-entity configs repeat device metadata; bundle has a single dev block.\n- Keys inside bundle: entities are keyed by unique_id; entity-centric uses object_id in topic paths.\n- Transport defaults: bundle may include qos/retain as top-level hints; per-entity uses transport options only.\n\nDevice-centric publish example\n\n```python\nfrom ha_mqtt_publisher.ha_discovery import Device, publish_device_config\n\ndevice = Device(app_config)\n\n# Choose topic device_id explicitly, or omit to use the first identifier\nok = publish_device_config(\n\tconfig=app_config,\n\tpublisher=publisher,\n\tdevice=device,\n\tdevice_id=\"living_room_bridge\",\n)\n```\n\nBundled device-centric publish (single message)\n\n```python\nfrom ha_mqtt_publisher.ha_discovery import Device, Sensor, publish_device_bundle\n\ndevice = Device(app_config)\ntemp = Sensor(app_config, device, name=\"Temperature\", unique_id=\"temp\", state_topic=\"room/t\")\nhumid = Sensor(app_config, device, name=\"Humidity\", unique_id=\"humid\", state_topic=\"room/h\")\n\n# Publishes one config message containing device (dev) and components (cmps)\npublish_device_bundle(\n\tconfig=app_config,\n\tpublisher=publisher,\n\tdevice=device,\n\tentities=[temp, humid],\n)\n```\n\n### One-time publication\n\n- Enabled by passing one_time_mode=True to publish_discovery_configs.\n- Tracks published topics in home_assistant.discovery_state_file.\n\n## Supported Home Assistant components\n\n### About \"Device\" (registry grouping)\n\n- Device is metadata included in each entity's discovery payload; it is not a standalone component or topic.\n- Home Assistant uses it to group entities in the Device Registry and display manufacturer/model, versions, and links.\n- Create one Device per physical/logical device and pass it to all related entities; removal happens when all related entities are removed.\n\n### Components\n\n| Type            | Component key       | Notes |\n|-----------------|---------------------|-------|\n| Sensor          | sensor              | state_topic required |\n| Binary Sensor   | binary_sensor       | state_topic required; device_class supported |\n| Switch          | switch              | command/state topics supported |\n| Light           | light               | payload_on/off defaults; command/state |\n| Cover           | cover               | payload_open/close/stop defaults |\n| Climate         | climate             | topic fields per HA spec |\n| Fan             | fan                 | payload_on/off defaults |\n| Lock            | lock                | payload_lock/unlock defaults |\n| Number          | number              | numeric set/get |\n| Select          | select              | options via extra attributes |\n| Text            | text                | text set/get |\n| Button          | button              | stateless trigger |\n| Device Tracker  | device_tracker      | presence/location topics |\n| Alarm Control   | alarm_control_panel | arm/disarm topics as applicable |\n| Camera          | camera              | image/stream topics as applicable |\n| Status Sensor   | sensor (helper)     | convenience entity for app status |\n\nNotes\n- Validation covers entity_category, availability_mode, sensor state_class, and device_class.\n- Additional allowed values can be provided via home_assistant.extra_allowed.\n\n## Testing\n\n```bash\npytest -q\n```\n\nEntity-centric verification snippet\n\n```python\nfrom ha_mqtt_publisher.ha_discovery import ensure_discovery\n\n# Verify per-entity discovery topics; republish any missing\nensure_discovery(\n\tconfig=app_config,\n\tpublisher=publisher,\n\tentities=[temp, humid, status],\n\tdevice=device,\n\tone_time_mode=True,\n)\n```\nSee also: examples/entity_verification.py\n\nEmit device bundle within publish_discovery_configs\n\n```python\npublish_discovery_configs(\n\tconfig=app_config,\n\tpublisher=publisher,\n\tentities=[temp, humid],\n\tdevice=device,\n\tone_time_mode=True,\n\temit_device_bundle=True,  # bundle first, then per-entity topics\n)\n```\n\n### Discovery verification (optional self-heal)\n\nIf you want the library to verify retained discovery topics exist on the broker and republish any that are missing, enable the verification pass when using one-time mode.\n\n- Config flags:\n  - home_assistant.ensure_discovery_on_startup: true|false (default false)\n  - home_assistant.ensure_discovery_timeout: float seconds (default 2.0)\n  - home_assistant.bundle_only_mode: true|false (default false). When true, verification checks only the device bundle topic and republishes it if missing.\n\nLightweight example\n\n```python\nfrom ha_mqtt_publisher.ha_discovery import ensure_discovery\n\n# Before publish_discovery_configs (optional; publish_discovery_configs will call this automatically\n# when one_time_mode=True and home_assistant.ensure_discovery_on_startup is true)\nensure_discovery(\n\tconfig=app_config,\n\tpublisher=publisher,\n\tentities=[temp, status],\n\tdevice=device,\n\ttimeout=app_config.get(\"home_assistant.ensure_discovery_timeout\", 2.0),\n\tone_time_mode=True,\n)\n```\n\nModern HA: bundle-only verification example\n\nIf your Home Assistant version supports device bundle configs and you set:\n\n```yaml\nhome_assistant:\n\tbundle_only_mode: true\n```\n\nYou can verify (and republish if missing) just the bundle topic:\n\n```python\nfrom ha_mqtt_publisher.ha_discovery import ensure_discovery\n\n# Assumes home_assistant.bundle_only_mode: true in your YAML\nensure_discovery(\n\t\tconfig=app_config,\n\t\tpublisher=publisher,\n\t\tentities=[temp, humid],  # included in the bundle\n\t\tdevice=device,\n\t\tdevice_id=\"living_room_bridge\",  # optional; defaults to first device identifier\n\t\tone_time_mode=True,\n)\n```\n\nSee also: examples/bundle_only_verification.py\n\n## Development\n\n- Install dev dependencies: pip install -e .[dev]\n- Lint and format: ruff check . && ruff format .\n\n## Troubleshooting\n\n- Connection refused with TLS/non-TLS port mismatch: ensure tls settings align with broker_port (1883 non-TLS, 8883 TLS).\n- Discovery not appearing: verify discovery_prefix and that MQTT messages are retained on config topics.\n\n## License\n\nMIT\n\n## Contributing\n\nIssues and pull requests are welcome in the GitHub repository.\n\n## Support\n\nOpen a GitHub issue for questions and problems.\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "An MQTT publisher package",
    "version": "0.3.2",
    "project_urls": {
        "Homepage": "https://github.com/ronschaeffer/ha_mqtt_publisher",
        "Repository": "https://github.com/ronschaeffer/ha_mqtt_publisher"
    },
    "split_keywords": [
        "mqtt",
        " publisher",
        " iot"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "9d5db88ae53acfb1170de1bb0cd46edacb98f998912569bd3be5ed456d11fe12",
                "md5": "79d98fbba32e0095231244900bf4eb17",
                "sha256": "4479e6ee23b608ce0d92ef19ed92537ea9ec9cccf8a70e88f46a4c081333315d"
            },
            "downloads": -1,
            "filename": "ha_mqtt_publisher-0.3.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "79d98fbba32e0095231244900bf4eb17",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 33587,
            "upload_time": "2025-08-11T18:31:15",
            "upload_time_iso_8601": "2025-08-11T18:31:15.361197Z",
            "url": "https://files.pythonhosted.org/packages/9d/5d/b88ae53acfb1170de1bb0cd46edacb98f998912569bd3be5ed456d11fe12/ha_mqtt_publisher-0.3.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "df8f36f5746181daffdbafbb56f0f3c782fbd1821c655462ab2ad673362c31ec",
                "md5": "45c86f6f67606334cb73cf2086f96bdd",
                "sha256": "f17e9e2d2eeffc00eb17a33a0c5ab91c2c393b5d81257a52c6ea932054f15be3"
            },
            "downloads": -1,
            "filename": "ha_mqtt_publisher-0.3.2.tar.gz",
            "has_sig": false,
            "md5_digest": "45c86f6f67606334cb73cf2086f96bdd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 31807,
            "upload_time": "2025-08-11T18:31:16",
            "upload_time_iso_8601": "2025-08-11T18:31:16.839357Z",
            "url": "https://files.pythonhosted.org/packages/df/8f/36f5746181daffdbafbb56f0f3c782fbd1821c655462ab2ad673362c31ec/ha_mqtt_publisher-0.3.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-08-11 18:31:16",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "ronschaeffer",
    "github_project": "ha_mqtt_publisher",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "ha-mqtt-publisher"
}
        
Elapsed time: 0.60733s