HA MQTT Publisher
[](https://pypi.org/project/ha-mqtt-publisher/)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
[](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[](https://pypi.org/project/ha-mqtt-publisher/)\n[](https://www.python.org/downloads/)\n[](https://opensource.org/licenses/MIT)\n[](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"
}