42 Commits
0.1.0 ... 0.2.2

Author SHA1 Message Date
7175522f4a custom_components/whatsapp_bridge_integration/sensor.py aktualisiert
0.2.2
2026-06-15 09:37:06 +00:00
3b99c037ae custom_components/whatsapp_bridge_integration/__init__.py aktualisiert
0.2.2
2026-06-15 09:36:42 +00:00
4f8a4304ba custom_components/whatsapp_bridge_integration/manifest.json aktualisiert
0.2.2
2026-06-15 09:36:19 +00:00
bcff98b44b CHANGELOG.md aktualisiert
0.2.2
2026-06-15 09:36:00 +00:00
40f74f9b8b CHANGELOG.md aktualisiert 2026-06-15 09:27:36 +00:00
5e1c07cecf CHANGELOG.md aktualisiert
0.2.1
2026-06-15 09:26:57 +00:00
2cfb881cc0 custom_components/whatsapp_bridge_integration/sensor.py aktualisiert
0.2.1
2026-06-15 09:26:10 +00:00
c5d216e458 custom_components/whatsapp_bridge_integration/__init__.py aktualisiert
0.2.1
2026-06-15 09:25:47 +00:00
4f08cbbd6d custom_components/whatsapp_bridge_integration/manifest.json aktualisiert
0.2.1
2026-06-15 09:25:26 +00:00
d09ff3d7cf custom_components/whatsapp_bridge_integration/sensor.py aktualisiert
0.2.0
2026-06-15 09:16:40 +00:00
fa7baddeb6 custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-06-15 09:16:13 +00:00
cff4183c46 custom_components/whatsapp_bridge_integration/manifest.json aktualisiert
0.2.0
2026-06-15 09:09:18 +00:00
719a4b5129 CHANGELOG.md
Add Changelog
2026-06-15 09:08:28 +00:00
c536a75f4c README.md aktualisiert 2026-04-14 16:19:46 +00:00
f346cea41a custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 14:02:20 +00:00
93b57878f9 custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 14:01:07 +00:00
fc826d7345 custom_components/whatsapp_bridge_integration/services.py aktualisiert 2026-04-14 14:00:54 +00:00
3002acc934 custom_components/whatsapp_bridge_integration/sensor.py aktualisiert 2026-04-14 14:00:36 +00:00
39df1ed0fa custom_components/whatsapp_bridge_integration/services.py aktualisiert 2026-04-14 13:59:19 +00:00
6b3a10bd33 custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 13:59:02 +00:00
ec6a9c6dd8 custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:48:34 +00:00
aa5a060aef custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 13:47:33 +00:00
7bea9bbbbb custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:43:28 +00:00
8dc6cfb7c1 custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 13:43:03 +00:00
b4fd722d8a custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 13:40:46 +00:00
20e6090f65 custom_components/whatsapp_bridge_integration/services.py hinzugefügt 2026-04-14 13:40:31 +00:00
d9ada23f3e custom_components/whatsapp_bridge_integration/sensor.py hinzugefügt 2026-04-14 13:40:10 +00:00
276d4a63cb custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-04-14 13:39:51 +00:00
adfe5dfe9d custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:29:15 +00:00
037cd9c9c7 custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-04-14 13:28:19 +00:00
498c059aa8 custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:24:38 +00:00
dbdf987501 custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-04-14 13:24:24 +00:00
370e33da7c custom_components/whatsapp_bridge_integration/services.yaml aktualisiert 2026-04-14 13:24:10 +00:00
5d5971ece0 custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:15:31 +00:00
8b9aacfefb custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-04-14 13:15:02 +00:00
c0800ce2bb custom_components/whatsapp_bridge_integration/config_flow.py aktualisiert 2026-04-14 13:14:45 +00:00
43b4c747e5 custom_components/whatsapp_bridge_integration/services.yaml hinzugefügt 2026-04-14 13:14:25 +00:00
61f36c7db7 custom_components/whatsapp_bridge_integration/__init__.py aktualisiert 2026-04-14 13:02:03 +00:00
9981adf5ac custom_components/whatsapp_bridge_integration/config_flow.py hinzugefügt 2026-04-14 13:01:48 +00:00
6b927b7e20 custom_components/whatsapp_bridge_integration/manifest.json aktualisiert 2026-04-14 13:01:29 +00:00
5122d59535 custom_components/whatsapp_bridge_integration/const.py hinzugefügt 2026-04-14 13:01:06 +00:00
f7240b4eb3 README.md aktualisiert 2026-04-14 12:35:23 +00:00
9 changed files with 417 additions and 59 deletions

20
CHANGELOG.md Normal file
View File

@@ -0,0 +1,20 @@
# CHANGELOG - Whatsapp Bridge Integration
## Release 0.2.2 - 2026-06-15
- **Architecture Change:** Introduced a dedicated "WhatsApp Bridge Hub" device. This central device acts as a single point of reference for global components, preventing UI clutter.
- **Fix:** Moved the global live status sensor and the virtual broadcast entity to the new WhatsApp Bridge Hub device. This ensures they remain fully visible and accessible across the Home Assistant UI, automations, and service calls without being duplicated across individual user accounts.
- **Improvement:** Maintained isolated, clean individual devices for each configured account profile (*Larissa, Rene, Work*).
## Release 0.2.1 - 2026-06-15
- **Fix:** Resolved device assignment duplication where a single global gateway card duplicated across all account tabs.
- **Fix:** Globalized the live status sensor and the broadcast entity to instantiate exactly once for the entire integration rather than incorrectly duplicating per account.
- **Optimization:** Code refactoring within `sensor.py` to prevent entity duplication when utilizing multiple profiles.
## Release 0.2.0 - 2026-06-15
- **New:** Setup of a main device (WhatsApp Bridge Gateway) in the Home Assistant device registry.
- **New:** Integrated global service `whatsapp_bridge.send_broadcast`, which operates independently of individual profile entities and can send messages to any number or group ID.
- **New:** Added status sensor (`sensor.whatsapp_bridge_status`). This queries the add-on's new API and provides the current connection status (`CONNECTED`, `WAITING_FOR_SCAN`, `INITIALIZING`, `OFFLINE`) directly within Home Assistant.
- **Improved:** The sensor is assigned to the new main device, which significantly simplifies automations (e.g., alert on connection loss).
## Release 0.1.7
- Previous stable version

117
README.md
View File

@@ -1,33 +1,112 @@
# WhatsApp Bridge Integration for Home Assistant 🚀
# WhatsApp Bridge for Home Assistant
This is the custom integration for the **Bahmcloud WhatsApp Bridge**. It allows you to manage multiple WhatsApp accounts as Home Assistant services, making it incredibly easy to send automated messages via YAML or Node-RED.
A professional, seamless integration to send WhatsApp messages through the **Bahmcloud Bridge**. This integration supports multiple profiles, dynamic entity-based selection, and broadcast capabilities with native Home Assistant support.
---
## 🚀 Installation
## 🛠️ Installation
### 1. Prerequisite: The Add-on
Before installing this integration, ensure that the **WhatsApp Bridge Add-on** is installed and running.
* [Link to your Add-on Repository]
* Open the Add-on Web UI and scan the QR code to link your WhatsApp account.
### 1. Install the Add-on
The **Bahmcloud WhatsApp Bridge Add-on** must be running for the integration to work:
1. Navigate to **Settings** -> **Add-ons**.
2. Click **Add-on Store** -> Three dots (top right) -> **Repositories**.
3. Add your Bahmcloud Repository URL.
4. Search for "WhatsApp Bridge", click **Install**, and **Start**.
### 2. Install the Integration
1. Copy the folder `whatsapp_bridge_integration` into your Home Assistant `config/custom_components/` directory.
2. Restart Home Assistant.
3. Go to **Settings** -> **Devices & Services**.
4. Click **Add Integration** and search for "WhatsApp Bridge Integration".
You can install the integration component via three methods:
* **HACS (Recommended):** Add this repository as a "Custom Repository" in HACS and click Install.
* **BCS (Bahmcloud Store):** Search for "WhatsApp Bridge" in the BCS interface.
* **Manual:** Copy the `custom_components/whatsapp_bridge_integration` folder into your `/config/custom_components/` directory and restart Home Assistant.
---
## 📖 How to use
## 👥 Configuration & Profiles
Once installed, the integration provides a new service (action) that you can use in any automation.
Add your WhatsApp accounts via the Home Assistant UI:
1. Go to **Settings** -> **Devices & Services**.
2. Click **Add Integration** and search for **WhatsApp Bridge**.
3. Follow the setup to add an **Account Name** (e.g., "Work") and your **Phone Number**.
4. **Repeat** this process for as many accounts as you need.
### Send a Message via YAML
You can call the service directly in your automations:
### Resulting Entities
The integration automatically creates sensors for your UI:
* `sensor.whatsapp_account_name`: An entity for each individual account.
* `sensor.whatsapp_broadcast_all`: A global entity to message all accounts at once.
---
## 🛠 Usage
### Action: `send_message`
This integration registers a service/action to send messages.
**Fields:**
* **Recipient:** Select an account from the dropdown, choose the "Broadcast" entity, or manually type a phone number.
* **Message:** The text content you wish to send.
#### YAML Example:
```yaml
action: whatsapp_bridge_integration.send_message
data:
number: "491701234567"
message: "The washing machine is finished! 🧺"
recipient: "sensor.whatsapp_rene"
message: "The garage door is still open!"
```
## ⚠️ Important Notes
Phone Numbers: Always use the international format without + or 00 (e.g., 49... for Germany).
Local Network: The integration communicates locally with the Add-on on port 3000.
Developed by Bahmcloud
---
## 🤖 Node-RED Integration
The integration is fully compatible with Node-RED. Since it uses native entities, you can use the standard *Action (Call Service)* node.
### Example: Smart Window Warning
Notify yourself if a window or door has been left open for 15 minutes.
1. *Trigger*: Use a `State Trigger` or `Device` node.
2. *Logic (Change Node)*: Use JSONata to create a dynamic message:
- *Set*: msg.payload
- *Value (JSONata)*: `payload.to_state.attributes.friendly_name & " has been open for 15 minutes!"`
3. WhatsApp Node (Action Node):
- *Action*: `whatsapp_bridge_integration.send_message`
- *Data (JSON)*:
```JSON
{
"recipient": "sensor.whatsapp_broadcast_all",
"message": "{{payload}}"
}
```
### Manual Tool Calling
To send a message to a number not saved as a profile, simply pass the raw phone number to the recipient field in your Node-RED JSON:
```JSON
{
"recipient": "491701234567",
"message": "Direct alert to external number."
}
```
---
# ✨ Features
- *Dynamic UI*: Accounts are automatically added to the recipient dropdown.
- *One-Click Broadcast*: Special entity to reach all configured profiles instantly.
- *Manual Fallback*: Clean UI remains flexible; type any number to bypass profiles.
- *Developer Friendly*: Designed for easy integration with Node-RED and HA Automations.
---
Maintained by Bahmcloud

View File

@@ -1,47 +1,55 @@
import logging
import requests
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
DOMAIN = "whatsapp_bridge_custom"
async def async_setup(hass: HomeAssistant, config: dict):
return True
# Definition der Service-Parameter
SERVICE_SEND_MESSAGE = "send_message"
ATTR_NUMBER = "number"
ATTR_MESSAGE = "message"
async def async_setup_entry(hass: HomeAssistant, entry):
"""Set up entry, create specific contact device, central hub and forward to platforms."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = entry.data
SERVICE_SCHEMA = vol.Schema({
vol.Required(ATTR_NUMBER): cv.string,
vol.Required(ATTR_MESSAGE): cv.string,
})
device_registry = dr.async_get(hass)
async def async_setup(hass, config):
"""Set up the WhatsApp Bridge integration."""
def send_whatsapp_msg(call):
"""Dienst-Funktion: Nachricht an das Add-on senden."""
number = call.data.get(ATTR_NUMBER)
message = call.data.get(ATTR_MESSAGE)
# Die interne URL zum Add-on (standardmäßig local-whatsapp-bridge)
# Port 3000 haben wir im Dockerfile definiert
url = "http://866fd2eb-whatsapp-bridge:3000/send"
try:
response = requests.post(url, json={
"number": number,
"message": message
}, timeout=10)
if response.status_code != 200:
_LOGGER.error("Fehler beim Senden: %s", response.text)
except Exception as e:
_LOGGER.error("Verbindung zum Add-on fehlgeschlagen: %s", str(e))
hass.services.async_register(
DOMAIN, SERVICE_SEND_MESSAGE, send_whatsapp_msg, schema=SERVICE_SCHEMA
# 1. Eigenes Gerät SPEZIFISCH für diesen Account/Kontakt anlegen
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, entry.entry_id)}, # Einzigartig pro Eintrag
name=f"WhatsApp {entry.title}",
manufacturer="Bahmcloud",
model="Kontakt-Endpunkt",
sw_version="0.2.2",
)
return True
# 2. Zentralen System-Hub NUR EINMALIG anlegen (wird an den ersten geladenen Account gekoppelt)
if not hass.data[DOMAIN].get("hub_device_created"):
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, "global_bridge_hub")},
name="WhatsApp Bridge Hub",
manufacturer="Bahmcloud",
model="Bridge Server",
sw_version="0.2.2",
)
hass.data[DOMAIN]["hub_device_created"] = True
# Forward the setup to the sensor platform to create entities
await hass.config_entries.async_forward_entry_setups(entry, ["sensor"])
# Registriere den Service (nur beim ersten Mal)
if not hass.services.has_service(DOMAIN, "send_message"):
from .services import async_setup_services
await async_setup_services(hass)
return True
async def async_unload_entry(hass: HomeAssistant, entry):
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, ["sensor"])
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@@ -0,0 +1,25 @@
from homeassistant import config_entries
import voluptuous as vol
from .const import DOMAIN, CONF_PHONE_NUMBER, CONF_ACCOUNT_NAME
class WhatsAppBridgeConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for WhatsApp Bridge."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""First step when adding the integration via UI."""
errors = {}
if user_input is not None:
return self.async_create_entry(
title=user_input[CONF_ACCOUNT_NAME],
data=user_input
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required(CONF_ACCOUNT_NAME, default="John Doe"): str,
vol.Required(CONF_PHONE_NUMBER, default="491701234567"): str,
}),
errors=errors,
)

View File

@@ -0,0 +1,3 @@
DOMAIN = "whatsapp_bridge_integration"
CONF_PHONE_NUMBER = "phone_number"
CONF_ACCOUNT_NAME = "account_name"

View File

@@ -4,6 +4,7 @@
"documentation": "https://git.bahmcloud.de/bahmcloud/Whatsapp-Bridge-Integration",
"dependencies": [],
"codeowners": ["@bahmcloud"],
"version": "0.1.0",
"iot_class": "local_polling"
"version": "0.2.2",
"iot_class": "local_polling",
"config_flow": true
}

View File

@@ -0,0 +1,157 @@
import logging
import async_timeout
import aiohttp
from datetime import timedelta
from homeassistant.components.sensor import SensorEntity
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=15)
async def async_setup_entry(hass, entry, async_add_entities):
"""Add sensors for WhatsApp accounts, live status, and a global broadcast sensor."""
# Coordinator für den echten Live-Status vom Add-on einrichten
async def _async_update_data():
url = "http://866fd2eb-whatsapp-bridge:3000/api/status"
async with aiohttp.ClientSession() as session:
try:
async with async_timeout.timeout(5):
async with session.get(url) as response:
if response.status != 200:
raise UpdateFailed(f"API meldet Fehler {response.status}")
return await response.json()
except Exception as err:
raise UpdateFailed(f"Add-on nicht erreichbar: {err}")
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,
name="whatsapp_bridge_status",
update_method=_async_update_data,
update_interval=SCAN_INTERVAL,
)
try:
await coordinator.async_config_entry_first_refresh()
except UpdateFailed:
_LOGGER.warning("Erster Status-Abruf fehlgeschlagen. Add-on läuft eventuell noch an.")
# Jeder Account bekommt immer seine eigene Entität
entities = [WhatsAppAccountEntity(entry)]
# Globale Entitäten (Status & Broadcast) NUR EINMALIG für die gesamte Integration anlegen
if not hass.data[DOMAIN].get("global_entities_registered"):
entities.append(WhatsAppStatusSensor(coordinator))
entities.append(WhatsAppBroadcastEntity())
hass.data[DOMAIN]["global_entities_registered"] = True
async_add_entities(entities, True)
class WhatsAppAccountEntity(SensorEntity):
"""Individual Account (e.g., Rene, Work)."""
def __init__(self, entry):
self._entry = entry
self._attr_name = f"WhatsApp {entry.title}"
self._attr_unique_id = f"{entry.entry_id}_status"
self._attr_icon = "mdi:whatsapp"
self._attr_extra_state_attributes = {
"phone_number": entry.data.get("phone_number"),
"is_broadcast": False
}
@property
def state(self):
return "Ready"
@property
def device_info(self) -> DeviceInfo:
"""Ordnet diesen Sensor seinem spezifischen Personen-Gerät zu."""
return DeviceInfo(
identifiers={(DOMAIN, self._entry.entry_id)},
)
class WhatsAppBroadcastEntity(SensorEntity):
"""Virtual 'All Accounts' Entity, geordnet unter dem zentralen Hub."""
def __init__(self):
self._attr_name = "WhatsApp Broadcast (All)"
self._attr_unique_id = "whatsapp_bridge_broadcast_all"
self._attr_icon = "mdi:whatsapp-arrow-up"
self._attr_extra_state_attributes = {
"is_broadcast": True
}
@property
def state(self):
return "Global"
@property
def device_info(self) -> DeviceInfo:
"""Ordnet diese Entität dem zentralen Hub-Gerät zu."""
return DeviceInfo(
identifiers={(DOMAIN, "global_bridge_hub")},
)
class WhatsAppStatusSensor(SensorEntity):
"""Echter Live-Status-Sensor, geordnet unter dem zentralen Hub."""
def __init__(self, coordinator):
self.coordinator = coordinator
self._attr_name = "WhatsApp Bridge Status"
self._attr_unique_id = "whatsapp_bridge_live_status_global"
self._attr_icon = "mdi:whatsapp"
@property
def should_poll(self) -> bool:
return False
@property
def available(self) -> bool:
return self.coordinator.last_update_success
@property
def state(self):
"""Gibt den dynamischen Status aus der Add-on API zurück."""
data = self.coordinator.data
if not data:
return "OFFLINE"
if data.get("isReady"):
return "CONNECTED"
elif data.get("hasQr"):
return "WAITING_FOR_SCAN"
elif data.get("isInitializing"):
return "INITIALIZING"
return "UNKNOWN"
@property
def extra_state_attributes(self):
"""Zusätzliche Attribute für erweitertes Debugging."""
data = self.coordinator.data
if data:
return {
"client_state": data.get("clientState", "UNKNOWN"),
"has_qr_code": data.get("hasQr", False)
}
return {}
@property
def device_info(self) -> DeviceInfo:
"""Ordnet diese Entität dem zentralen Hub-Gerät zu."""
return DeviceInfo(
identifiers={(DOMAIN, "global_bridge_hub")},
)
async def async_added_to_hass(self):
"""Registrieren beim Live-Coordinator."""
self.async_on_remove(
self.coordinator.async_add_listener(self.async_write_ha_state)
)

View File

@@ -0,0 +1,44 @@
import requests
import logging
from homeassistant.core import HomeAssistant, ServiceCall
from .const import DOMAIN, CONF_PHONE_NUMBER
_LOGGER = logging.getLogger(__name__)
async def async_setup_services(hass: HomeAssistant):
async def handle_send_message(call: ServiceCall):
message = call.data.get("message")
recipient = call.data.get("recipient")
targets = []
entries = hass.config_entries.async_entries(DOMAIN)
# 1. Prüfen, ob es eine Entity-ID ist
if recipient.startswith("sensor."):
state = hass.states.get(recipient)
if state:
# Ist es die 'All Accounts' Entität?
if state.attributes.get("is_broadcast"):
for e in entries:
targets.append(e.data.get(CONF_PHONE_NUMBER))
else:
# Es ist ein einzelner Account
targets.append(state.attributes.get("phone_number"))
# 2. Fallback: Manuelle Nummer (wenn keine sensor-ID übergeben wurde)
else:
targets.append(recipient)
# Senden
for num in targets:
if not num: continue
url = "http://866fd2eb-whatsapp-bridge:3000/send"
try:
await hass.async_add_executor_job(
lambda: requests.post(url, json={"number": num, "message": message}, timeout=10)
)
_LOGGER.info("WhatsApp sent to %s", num)
except Exception as e:
_LOGGER.error("Error sending to %s: %s", num, str(e))
hass.services.async_register(DOMAIN, "send_message", handle_send_message)

View File

@@ -0,0 +1,21 @@
send_message:
name: Send Message
description: Sends a WhatsApp message via the Bridge.
fields:
recipient:
name: Recipient
description: Select an Account, the Broadcast entity, or type a manual number.
required: true
selector:
# Dies zeigt nun "WhatsApp Rene", "WhatsApp Work" UND "WhatsApp Broadcast (All)" an.
entity:
filter:
integration: whatsapp_bridge_integration
domain: sensor
message:
name: Message
description: Your message text.
required: true
selector:
text:
multiline: true