Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 40 additions & 52 deletions custom_components/protocol_wizard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,64 @@
#-- base init.py protocol wizard
#------------------------------------------
"""The Protocol Wizard integration."""
import asyncio
import shutil
import logging
import os
import asyncio
import re
import shutil
from datetime import timedelta

from homeassistant.helpers import device_registry as dr, entity_registry as er, config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, ServiceCall
from pymodbus.client import AsyncModbusSerialClient, AsyncModbusTcpClient, AsyncModbusUdpClient
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.service import SupportsResponse
from pymodbus.client import (
AsyncModbusSerialClient,
AsyncModbusTcpClient,
AsyncModbusUdpClient,
)
from datetime import timedelta
# Import protocol registry and plugins
from .protocols import ProtocolRegistry
from .protocols.modbus import ModbusClient
from .protocols.snmp import SNMPClient
from .protocols.mqtt import MQTTClient
from .protocols.bacnet.client import BACnetClient
from .template_utils import ensure_user_template_dirs, load_template

from .const import (
CONF_BACNET_DEVICES,
CONF_BAUDRATE,
CONF_BYTESIZE,
CONF_CONNECTION_TYPE,
CONF_ENTITIES,
CONF_HOST,
CONF_NAME,
CONF_PARITY,
CONF_PORT,
CONF_PROTOCOL,
CONF_PROTOCOL_BACNET,
CONF_PROTOCOL_MODBUS,
CONF_PROTOCOL_MQTT,
CONF_PROTOCOL_SNMP,
CONF_REGISTERS,
CONF_SERIAL_PORT,
CONF_SLAVE_ID,
CONF_SLAVES,
CONF_STOPBITS,
CONF_TEMPLATE,
CONF_TEMPLATE_APPLIED,
CONF_UPDATE_INTERVAL,
CONNECTION_TYPE_IP,
CONF_NAME,
CONNECTION_TYPE_SERIAL,
CONNECTION_TYPE_TCP,
CONNECTION_TYPE_IP,
CONNECTION_TYPE_UDP,
CONNECTION_TYPE_TCP,
DEFAULT_BAUDRATE,
DEFAULT_BYTESIZE,
DEFAULT_PARITY,
DEFAULT_STOPBITS,
DOMAIN,
CONF_PROTOCOL_MODBUS,
CONF_PROTOCOL_SNMP,
CONF_PROTOCOL_MQTT,
CONF_PROTOCOL_BACNET,
CONF_PROTOCOL,
CONF_TEMPLATE,
CONF_TEMPLATE_APPLIED,
CONF_ENTITIES,
CONF_REGISTERS,
CONF_SLAVES,
CONF_BACNET_DEVICES,
SIGNAL_ENTITY_SYNC,
)

# Import protocol registry and plugins
from .protocols import ProtocolRegistry
from .protocols.bacnet.client import BACnetClient
from .protocols.modbus import ModbusClient
from .protocols.mqtt import MQTTClient
from .protocols.snmp import SNMPClient
from .template_utils import ensure_user_template_dirs, load_template

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -93,7 +87,7 @@ def install():
else:
_LOGGER.warning("Frontend source file missing at %s", source_path)

except OSError as err:
except Exception as err:
_LOGGER.error("Failed to install frontend resource: %s", err)

await hass.async_add_executor_job(install)
Expand Down Expand Up @@ -428,7 +422,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
else:
_LOGGER.error("Protocol %s not yet implemented", protocol_name)
return False
except (OSError, ConnectionError, TimeoutError, ValueError) as err:
except Exception as err:
_LOGGER.error("Failed to create client for %s: %s", protocol_name, err)
return False

Expand Down Expand Up @@ -545,7 +539,7 @@ async def _load_template_into_options(

hass.config_entries.async_update_entry(entry, options=new_options)

except (OSError, KeyError, TypeError, ValueError) as err:
except Exception as err:
_LOGGER.error("Failed to load template %s: %s", template_name, err)


Expand Down Expand Up @@ -619,13 +613,7 @@ def _create_snmp_client(config: dict) -> SNMPClient:

def _create_mqtt_client(config: dict) -> MQTTClient:
"""Create MQTT client (no caching needed - manages its own connection)."""
from .protocols.mqtt import (
CONF_BROKER,
CONF_PASSWORD,
CONF_USERNAME,
DEFAULT_PORT,
MQTTClient,
)
from .protocols.mqtt import MQTTClient, CONF_BROKER, CONF_USERNAME, CONF_PASSWORD, DEFAULT_PORT

return MQTTClient(
broker=config[CONF_BROKER],
Expand Down Expand Up @@ -816,7 +804,7 @@ async def handle_add_entity(call: ServiceCall):

# Add optional fields if provided
for field in ["format", "options", "device_class", "state_class", "entity_category", "icon", "min", "max", "step"]:
if call.data.get(field):
if field in call.data and call.data[field]:
new_entity[field] = call.data[field]

# Check for duplicates
Expand Down Expand Up @@ -880,8 +868,8 @@ async def handle_add_entity(call: ServiceCall):
}

except Exception as err:
_LOGGER.exception("Failed to add entity")
raise HomeAssistantError(f"Failed to add entity: {err!s}") from err
_LOGGER.error("Failed to add entity: %s", err, exc_info=True)
raise HomeAssistantError(f"Failed to add entity: {str(err)}") from err

async def handle_write_register(call: ServiceCall):
"""Generic write service (protocol-agnostic) with detailed logging."""
Expand Down Expand Up @@ -912,8 +900,8 @@ async def handle_write_register(call: ServiceCall):
raise HomeAssistantError(f"Write failed for address {address}")

except Exception as err:
_LOGGER.exception("Unexpected exception in write_register service for address %s", address)
raise HomeAssistantError(f"Write failed for address {address}: {err!s}") from err
_LOGGER.error("Unexpected exception in write_register service for address %s: %s", address, err, exc_info=True)
raise HomeAssistantError(f"Write failed for address {address}: {str(err)}") from err

async def handle_read_register(call: ServiceCall):
"""Generic read service (protocol-agnostic)."""
Expand Down Expand Up @@ -1066,7 +1054,7 @@ async def handle_read_bacnet(call: ServiceCall):
address=address,
entity_config=entity_config,
)
except (OSError, ConnectionError, TimeoutError) as err:
except Exception as err:
_LOGGER.debug("BACnet Read failed with error: %s", err)
if value is None:
raise HomeAssistantError(f"Failed to read BACnet address {address}")
Expand Down Expand Up @@ -1105,7 +1093,7 @@ async def handle_write_bacnet(call: ServiceCall):
value=value,
entity_config=entity_config,
)
except (OSError, ConnectionError, TimeoutError) as err:
except Exception as err:
_LOGGER.debug("BACnet write failed with error: %s", err)

if not success:
Expand Down Expand Up @@ -1188,7 +1176,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not still_used:
try:
await client.disconnect()
except Exception as err: # noqa: BLE001
except Exception as err:
_LOGGER.debug("Error closing Modbus client: %s", err)
else:
# Other protocols (SNMP, MQTT, etc.)
Expand All @@ -1205,7 +1193,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if not still_used:
try:
await client.disconnect()
except Exception as err: # noqa: BLE001
except Exception as err:
_LOGGER.debug("Error closing client: %s", err)

unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
Expand Down
Loading
Loading