Skip to content

Commit

Permalink
0.10.3.1 - Moved load+unload, fixed restart, diag includes sensor mod…
Browse files Browse the repository at this point in the history
…el ident
  • Loading branch information
davesmeghead committed Jan 19, 2025
1 parent 4d031c2 commit 978b0eb
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 55 deletions.
16 changes: 15 additions & 1 deletion custom_components/visonic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@

_LOGGER = logging.getLogger(__name__)

PLATFORMS = [
Platform.ALARM_CONTROL_PANEL,
Platform.BINARY_SENSOR,
Platform.IMAGE,
Platform.SELECT,
Platform.SIREN,
Platform.SENSOR,
Platform.SWITCH,
]


CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)

# the 6 schemas for the HA service calls
Expand Down Expand Up @@ -498,6 +509,8 @@ def handle_core_config_updated(event):
# add update listener to unload. The update listener is used when the user edits an existing configuration.
entry.async_on_unload(entry.add_update_listener(update_listener))

await hass.config_entries.async_forward_entry_setups( entry, PLATFORMS )

_LOGGER.debug(f"[Visonic Setup] Returning True for entry id {entry.entry_id}")
# return true to indicate success
return True
Expand Down Expand Up @@ -591,7 +604,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: VisonicConfigEntry):
if data.client is not None:
p = data.client.getPanelID()
# stop all activity in the client
unload_ok = await data.client.async_service_panel_stop()
await data.client.async_service_panel_stop()
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if entry.entry_id in hass.data[VisonicConfigKey]:
hass.data[VisonicConfigKey].pop(entry.entry_id)
Expand Down
54 changes: 26 additions & 28 deletions custom_components/visonic/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
PIN_REGEX,
)

CLIENT_VERSION = "0.10.3.0"
CLIENT_VERSION = "0.10.3.1"

MAX_CLIENT_LOG_ENTRIES = 300

Expand Down Expand Up @@ -355,7 +355,7 @@ def _initialise(self):

self.rationalised_ha_devices = False

self.loaded_platforms = set()
#self.loaded_platforms = set()

self.onChangeHandler = []

Expand Down Expand Up @@ -450,7 +450,7 @@ def getStrLog(self):
return self.strlog

def getEntryID(self):
return self.entry.entry_id
return self.entry.entry_id if self.entry is not None else ""

def getPanelID(self):
return self.panelident
Expand Down Expand Up @@ -773,15 +773,15 @@ def process_panel_event_log(self, entry: AlLogPanelEvent):
# This is not called from anywhere, use it for debug purposes and/or to clear all entities from HA
def printAllEntities(self, delete_as_well : bool = False):
entity_reg = er.async_get(self.hass)
entity_entries = er.async_entries_for_config_entry(entity_reg, self.entry.entry_id)
entity_entries = er.async_entries_for_config_entry(entity_reg, self.getEntryID())
for damn in entity_entries:
_LOGGER.debug(f" entity {damn}")
if delete_as_well:
entity_reg.async_remove(damn.entity_id)

# clear out all devices from the registry to recreate them, if the user has added/removed devices then this ensures that its a clean start
device_reg = dr.async_get(self.hass)
device_entries = dr.async_entries_for_config_entry(device_reg, self.entry.entry_id)
device_entries = dr.async_entries_for_config_entry(device_reg, self.getEntryID())
for damn in device_entries:
_LOGGER.debug(f" device {damn}")
if delete_as_well:
Expand All @@ -791,15 +791,12 @@ def printAllEntities(self, delete_as_well : bool = False):
platforms = ep.async_get_platforms(self.hass, DOMAIN)
_LOGGER.debug(f" platforms {platforms}")

async def _setupVisonicEntity(self, platform, domain, param = None):
async def _setupVisonicEntity(self, domain, param = None):
"""Setup a platform and add an entity using the dispatcher."""
if platform not in self.loaded_platforms:
self.loaded_platforms.add(platform)
await self.hass.async_create_task(self.hass.config_entries.async_forward_entry_setups( self.entry, [ platform ] ) )
if param is None:
async_dispatcher_send( self.hass, f"{DOMAIN}_{self.entry.entry_id}_add_{domain}" )
async_dispatcher_send( self.hass, f"{DOMAIN}_{self.getEntryID()}_add_{domain}" )
else:
async_dispatcher_send( self.hass, f"{DOMAIN}_{self.entry.entry_id}_add_{domain}", param )
async_dispatcher_send( self.hass, f"{DOMAIN}_{self.getEntryID()}_add_{domain}", param )

def onNewSwitch(self, dev: AlSwitchDevice):
asyncio.ensure_future(self.async_onNewSwitch(dev), loop=self.hass.loop)
Expand All @@ -824,7 +821,7 @@ async def async_onNewSwitch(self, dev: AlSwitchDevice):
if dev not in self.x10_list:
self.logstate_debug(f"X10 Switch list {dev.getDeviceID()=}")
self.x10_list.append(dev)
await self._setupVisonicEntity(Platform.SWITCH, SWITCH_DOMAIN, dev)
await self._setupVisonicEntity(SWITCH_DOMAIN, dev)
else:
self.logstate_debug(f"X10 Device {dev.getDeviceID()} already in the list")

Expand All @@ -839,11 +836,11 @@ async def _async_setupAlarmPanel(self):
self._createdAlarmPanel = True
if self.DisableAllCommands:
self.logstate_debug("Creating Sensor for Alarm indications")
await self._setupVisonicEntity(Platform.SENSOR, SENSOR_DOMAIN)
await self._setupVisonicEntity(SENSOR_DOMAIN)
else:
self.logstate_debug("Creating Alarm Panel Entity")
await self._setupVisonicEntity(Platform.ALARM_CONTROL_PANEL, ALARM_PANEL_DOMAIN)
await self._setupVisonicEntity(Platform.SIREN, SIREN_DOMAIN)
await self._setupVisonicEntity(ALARM_PANEL_DOMAIN)
await self._setupVisonicEntity(SIREN_DOMAIN)

def onNewSensor(self, sensor: AlSensorDevice):
asyncio.ensure_future(self.async_onNewSensor(sensor), loop=self.hass.loop)
Expand All @@ -868,10 +865,10 @@ async def async_onNewSensor(self, sensor: AlSensorDevice):
if sensor not in self.sensor_list:
self.logstate_debug("Adding Sensor %s", sensor)
self.sensor_list.append(sensor)
await self._setupVisonicEntity(Platform.BINARY_SENSOR, BINARY_SENSOR_DOMAIN, sensor)
await self._setupVisonicEntity(BINARY_SENSOR_DOMAIN, sensor)
if not self.DisableAllCommands: # and self.toBool(self.config.get(CONF_ENABLE_SENSOR_BYPASS, False))
# The connection to the panel allows interaction with the sensor, including the arming/bypass of the sensors
await self._setupVisonicEntity(Platform.SELECT, SELECT_DOMAIN, sensor)
await self._setupVisonicEntity(SELECT_DOMAIN, sensor)
else:
self.logstate_debug(f"Sensor {sensor.getDeviceID()} already in the lists")
if not self.DisableAllCommands and sensor.getDeviceID() not in self.image_list and sensor.getSensorType() == AlSensorType.CAMERA:
Expand All @@ -887,7 +884,7 @@ async def create_image_entity(self, sensor):
if sensor.getDeviceID() not in self.image_list and sensor.getSensorType() == AlSensorType.CAMERA:
self.image_list.append(sensor.getDeviceID())
# The connection to the panel allows interaction with the sensor, including asking to get the image from a camera
await self._setupVisonicEntity(Platform.IMAGE, IMAGE_DOMAIN, sensor)
await self._setupVisonicEntity(IMAGE_DOMAIN, sensor)

def onChange(self, callback : Callable, partition : int | None = None, panel_entity_name : str | None = None):
if panel_entity_name is not None:
Expand Down Expand Up @@ -999,7 +996,7 @@ def filterDevicebyPanelIdent( devices : list, p : int ) -> list:
device_reg = dr.async_get(self.hass)

# Get a list of Home Assistant Visonic devices asociated with this config
device_entries = dr.async_entries_for_config_entry(device_reg, self.entry.entry_id)
device_entries = dr.async_entries_for_config_entry(device_reg, self.getEntryID())

#for device in device_entries:
# self.logstate_debug(f" HA Device BEFORE {device}")
Expand Down Expand Up @@ -1028,7 +1025,7 @@ def filterDevicebyPanelIdent( devices : list, p : int ) -> list:
device_reg.async_remove_device(device.id)

# Get the entities that are associated with this config
entity_entries = er.async_entries_for_config_entry(entity_reg, self.entry.entry_id)
entity_entries = er.async_entries_for_config_entry(entity_reg, self.getEntryID())

#for entity in entity_entries:
# self.logstate_debug(f" HA Entity BEFORE {entity}")
Expand Down Expand Up @@ -1890,10 +1887,10 @@ async def async_service_panel_stop(self) -> bool:
# stop the usb/ethernet comms with the panel
await self.service_comms_stop()

self.logstate_debug(f"Unloading platforms {self.loaded_platforms=} Entry id={self.entry.entry_id} ")
#self.logstate_debug(f"Unloading platforms {self.loaded_platforms=} Entry id={self.getEntryID()} ")
#self.printAllEntities()
unload_ok = await self.hass.config_entries.async_unload_platforms(self.entry, self.loaded_platforms)
self.logstate_debug(f"Unloading complete {unload_ok=}")
unload_ok = True # await self.hass.config_entries.async_unload_platforms(self.entry, self.loaded_platforms)
#self.logstate_debug(f"Unloading complete {unload_ok=}")
#self.printAllEntities()

# cancel the task from within HA
Expand Down Expand Up @@ -1955,7 +1952,7 @@ async def service_comms_stop(self):
await asyncio.sleep(0.5)
# not a mistake, wait a bit longer to make sure it's closed as we get no feedback (we only get the fact that the queue is empty)

async def async_service_panel_reconnect(self, call):
async def async_service_panel_reconnect(self, call=None):
"""Service call to re-connect the connection."""
if not self.isPanelConnected():
raise HomeAssistantError(
Expand All @@ -1966,10 +1963,11 @@ async def async_service_panel_reconnect(self, call):
}
)

if call.context.user_id:
#self.logstate_debug(f"Checking user information for permissions: {call.context.user_id}")
# Check security permissions (that this user has access to the alarm panel entity)
await self._checkUserPermission(call, POLICY_CONTROL, Platform.ALARM_CONTROL_PANEL + "." + slugify(self.getAlarmPanelUniqueIdent()))
if call is not None:
if call.context.user_id:
#self.logstate_debug(f"Checking user information for permissions: {call.context.user_id}")
# Check security permissions (that this user has access to the alarm panel entity)
await self._checkUserPermission(call, POLICY_CONTROL, Platform.ALARM_CONTROL_PANEL + "." + slugify(self.getAlarmPanelUniqueIdent()))

self.logstate_debug(f"Stopping Client and Reconnecting to Visonic Panel {self.getPanelID()}")
#self.logstate_debug("User has requested visonic panel reconnection")
Expand Down
2 changes: 1 addition & 1 deletion custom_components/visonic/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
"loggers": ["visonic"],
"requirements": ["Pillow", "pyserial_asyncio"],
"single_config_entry": false,
"version": "0.10.3.0"
"version": "0.10.3.1"
}

46 changes: 23 additions & 23 deletions custom_components/visonic/pyhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,32 +253,32 @@ def __str__(self):
pt = ""
for i in self.partition:
pt = pt + str(i) + " "
stypestr = ""
if self.stype is not None and self.stype != AlSensorType.UNKNOWN:
stypestr = titlecase(str(self.stype).replace("_"," "))
elif self.sid is not None:
stypestr = "Unk " + str(self.sid)
else:
stypestr = "Unknown"
#stypestr = ""
#if self.stype is not None and self.stype != AlSensorType.UNKNOWN:
# stypestr = titlecase(str(self.stype).replace("_"," "))
#elif self.sid is not None:
# stypestr = "Unk " + str(self.sid)
#else:
# stypestr = "Unknown"
strn = ""
strn = strn + ("id=None" if self.id == None else f"id={self.id:<2}")
#strn = strn + (" Zone=None" if self.dname == None else f" Zone={self.dname[:4]:<4}")
strn = strn + (f" Type={stypestr:<8}")
# temporarily miss it out to shorten the line in debug messages strn = strn + (" model=None" if self.model == None else f" model={self.model[:14]:<8}")
# temporarily miss it out to shorten the line in debug messages strn = strn + (" sid=None" if self.sid == None else f" sid={self.sid:<3}")
# temporarily miss it out to shorten the line in debug messages strn = strn + (" ztype=None" if self.ztype == None else f" ztype={self.ztype:<2}")
strn = strn + (" Loc=None " if self.zname == None else f" Loc={self.zname[:14]:<14}")
#strn = strn + (f" Type={stypestr:<8}")
strn = strn + (" partition=None " if self.partition == None else f" partition={pt:<7}")
strn = strn + (" bypass=- " if self.bypass == None else f" bypass={self.bypass:<2}")
strn = strn + (" lowbatt=- " if self.lowbatt == None else f" lowbatt={self.lowbatt:<2}")
strn = strn + (" status=- " if self.status == None else f" status={self.status:<2}")
strn = strn + (" tamper=- " if self.tamper == None else f" tamper={self.tamper:<2}")
strn = strn + (" enrolled=- " if self.enrolled == None else f" enrolled={self.enrolled:<2}")
strn = strn + (" triggered=- " if self.triggered == None else f" triggered={self.triggered:<2}")
strn = strn + (" ztamper=- " if self.ztamper == None else f" ztamper={self.ztamper:<2}")
strn = strn + (" ztrip=- " if self.ztrip == None else f" ztrip={self.ztrip:<2}")
strn = strn + (" stype=Undefined" if self.stype == None else f" stype={str(self.stype):<12}")
strn = strn + (" model=None" if self.model == None else f" model={self.model[:12]:<12}")
strn = strn + (" sid=None" if self.sid == None else f" sid={self.sid:<3}")
strn = strn + (" ztype=None" if self.ztype == None else f" ztype={self.ztype:<2}")
strn = strn + (" Loc=None " if self.zname == None else f" Loc={self.zname[:14]:<14}")
strn = strn + (" ztypeName=None " if self.ztypeName == None else f" ztypeName={self.ztypeName[:10]:<10}")
strn = strn + (" ztamper=--" if self.ztamper == None else f" ztamper={self.ztamper:<2}")
strn = strn + (" ztrip=--" if self.ztrip == None else f" ztrip={self.ztrip:<2}")
strn = strn + (" zchime=None " if self.zchime == None else f" zchime={self.zchime:<16}")
strn = strn + (" partition=None " if self.partition == None else f" partition={pt:<7}")
strn = strn + (" bypass=--" if self.bypass == None else f" bypass={self.bypass:<2}")
strn = strn + (" lowbatt=--" if self.lowbatt == None else f" lowbatt={self.lowbatt:<2}")
strn = strn + (" status=--" if self.status == None else f" status={self.status:<2}")
strn = strn + (" tamper=--" if self.tamper == None else f" tamper={self.tamper:<2}")
strn = strn + (" enrolled=--" if self.enrolled == None else f" enrolled={self.enrolled:<2}")
strn = strn + (" triggered=--" if self.triggered == None else f" triggered={self.triggered:<2}")
strn = strn + (" zchime=None " if self.zchime == None else f" zchime={self.zchime:<13}")
strn = strn + ("" if self.temperature == None else f" temperature={self.temperature:<3}")
strn = strn + ("" if self.luminance == None else f" luminance={self.luminance:<3}")

Expand Down
4 changes: 2 additions & 2 deletions custom_components/visonic/pyvisonic.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def convertByteArray(s) -> bytearray:
from pyhelper import (toString, MyChecksumCalc, AlImageManager, ImageRecord, titlecase, AlPanelInterfaceHelper,
AlSensorDeviceHelper, AlSwitchDeviceHelper)

PLUGIN_VERSION = "1.8.0.0"
PLUGIN_VERSION = "1.8.0.1"

# Obfuscate sensitive data, regardless of the other Debug settings.
# Setting this to True limits the logging of messages sent to the panel to CMD or NONE
Expand Down Expand Up @@ -3480,7 +3480,7 @@ def _updateSensor(self, zone) -> bool:

if device_type is not None:
sensorType = AlSensorType.UNKNOWN
sensorModel = "Model Unknown"
sensorModel = "Unknown"

if self.isPowerMaster(): # PowerMaster models
if device_type in pmZoneSensorMaster:
Expand Down

0 comments on commit 978b0eb

Please sign in to comment.