1- """The BlueBubbles integration."""
1+ """Config flow for BlueBubbles integration."""
22import logging
3- import re
43
54import aiohttp
5+ import voluptuous as vol
66
7- from homeassistant .config_entries import ConfigEntry
8- from homeassistant .core import HomeAssistant , ServiceCall
7+ from homeassistant import config_entries
8+ from homeassistant .const import CONF_HOST
9+ from homeassistant .helpers import selector
910
10- from .const import CONF_HOST , CONF_PASSWORD , CONF_SSL , DOMAIN
11+ from .const import CONF_PASSWORD , CONF_SSL , DOMAIN
1112
1213_LOGGER = logging .getLogger (__name__ )
1314
14- PLATFORMS : list [str ] = []
1515
16-
17- async def async_setup_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
18- """Set up BlueBubbles from a config entry."""
19-
20- hass .data .setdefault (DOMAIN , {})
21- hass .data [DOMAIN ][entry .entry_id ] = entry .data
22-
23- async def send_message (service_call : ServiceCall ) -> None :
24- """Handle the send_message service."""
25- conf = entry .data
26- host = conf [CONF_HOST ]
27- password = conf [CONF_PASSWORD ]
28- ssl = conf [CONF_SSL ]
29- private_api = conf .get ("private_api" , False )
30-
31- addresses_str = service_call .data .get ("addresses" , "" ).strip ()
32- message = service_call .data .get ("message" , "" ).strip ()
33-
34- if not addresses_str :
35- raise ValueError ("At least one address is required" )
36- if not message :
37- raise ValueError ("Message is required" )
38-
39- # Split by , or ; and trim
40- addresses = [n .strip () for n in re .split (r'[,;]' , addresses_str ) if n .strip ()]
41-
42- if not addresses :
43- raise ValueError ("No valid addresses provided" )
44-
45- url = f"{ host } /api/v1/chat/new"
46- params = {"password" : password }
47- method = "private-api" if private_api else "apple-script"
48- payload = {"addresses" : addresses , "message" : message , "method" : method }
49-
50- try :
51- async with aiohttp .ClientSession () as session , session .post (
52- url ,
53- json = payload ,
54- params = params ,
55- ssl = ssl
56- ) as response :
57- response .raise_for_status ()
58- _LOGGER .debug ("Message sent successfully" )
59- except aiohttp .ClientError as err :
60- _LOGGER .error ("Error sending message: %s. Payload: %s" , err , payload )
61- raise
62-
63- hass .services .async_register (DOMAIN , "send_message" , send_message )
64-
65- return True
66-
67-
68- async def async_unload_entry (hass : HomeAssistant , entry : ConfigEntry ) -> bool :
69- """Unload a config entry."""
70- hass .services .async_remove (DOMAIN , "send_message" )
71- hass .data [DOMAIN ].pop (entry .entry_id )
72- return True
16+ class ConfigFlow (config_entries .ConfigFlow , domain = DOMAIN ):
17+ """Handle a config flow for BlueBubbles."""
18+
19+ VERSION = 1
20+
21+ async def async_step_user (self , user_input = None ) -> config_entries .ConfigFlowResult :
22+ """Handle the initial step."""
23+
24+ if user_input is not None :
25+ errors = {}
26+
27+ host = user_input [CONF_HOST ]
28+ password = user_input [CONF_PASSWORD ]
29+ ssl = user_input [CONF_SSL ]
30+ url = f"{ host } /api/v1/server/info"
31+ params = {"password" : password }
32+
33+ try :
34+ async with aiohttp .ClientSession () as session , session .get (
35+ url ,
36+ params = params ,
37+ ssl = ssl
38+ ) as response :
39+ response .raise_for_status ()
40+ json_data = await response .json ()
41+ if json_data .get ("status" ) != 200 :
42+ raise ValueError (f"Invalid status in response: { json_data .get ('status' )} " )
43+ data = json_data ["data" ]
44+ private_api = data ["private_api" ]
45+ detected_imessage = data ["detected_imessage" ]
46+ entry_data = user_input .copy ()
47+ entry_data ["private_api" ] = private_api
48+ _LOGGER .debug ("Successfully connected to BlueBubbles" )
49+ return self .async_create_entry (title = detected_imessage , data = entry_data )
50+ except (aiohttp .ClientError , ValueError , KeyError ) as err :
51+ _LOGGER .error ("Error connecting to BlueBubbles: %s" , err )
52+ errors ["base" ] = "cannot_connect"
53+ except Exception as err :
54+ _LOGGER .error ("Unexpected error: %s" , err )
55+ errors ["base" ] = "unknown"
56+
57+ return self .async_show_form (
58+ step_id = "user" ,
59+ data_schema = vol .Schema (
60+ {
61+ vol .Required (CONF_HOST ): selector .TextSelector (
62+ selector .TextSelectorConfig (type = selector .TextSelectorType .URL )
63+ ),
64+ vol .Required (CONF_PASSWORD ): selector .TextSelector (
65+ selector .TextSelectorConfig (type = selector .TextSelectorType .PASSWORD )
66+ ),
67+ vol .Optional (CONF_SSL , default = False ): selector .BooleanSelector (),
68+ }
69+ ),
70+ errors = errors ,
71+ )
72+
73+ return self .async_show_form (
74+ step_id = "user" ,
75+ data_schema = vol .Schema (
76+ {
77+ vol .Required (CONF_HOST ): selector .TextSelector (
78+ selector .TextSelectorConfig (type = selector .TextSelectorType .URL )
79+ ),
80+ vol .Required (CONF_PASSWORD ): selector .TextSelector (
81+ selector .TextSelectorConfig (type = selector .TextSelectorType .PASSWORD )
82+ ),
83+ vol .Optional (CONF_SSL , default = False ): selector .BooleanSelector (),
84+ }
85+ ),
86+ errors = {},
87+ )
0 commit comments