mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 08:26:41 +01:00
Bump satel_integra to 1.0.0 (#164257)
This commit is contained in:
@@ -2,8 +2,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||
from homeassistant.helpers.entity_registry import RegistryEntry, async_migrate_entries
|
||||
|
||||
@@ -55,7 +55,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: SatelConfigEntry) -> boo
|
||||
coordinator_outputs=coordinator_outputs,
|
||||
coordinator_partitions=coordinator_partitions,
|
||||
)
|
||||
|
||||
async def async_close_connection(event: Event) -> None:
|
||||
"""Close Satel Integra connection on HA Stop."""
|
||||
await client.async_close()
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, async_close_connection)
|
||||
)
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
device_registry.async_get_or_create(
|
||||
@@ -74,7 +82,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: SatelConfigEntry) -> bo
|
||||
|
||||
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
|
||||
runtime_data = entry.runtime_data
|
||||
runtime_data.client.close()
|
||||
await runtime_data.client.async_close()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from satel_integra.satel_integra import AlarmState
|
||||
from satel_integra import AlarmState
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
AlarmControlPanelEntity,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
from collections.abc import Callable
|
||||
|
||||
from satel_integra.satel_integra import AsyncSatel
|
||||
from satel_integra import AsyncSatel
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import (
|
||||
@@ -61,18 +61,12 @@ class SatelClient:
|
||||
|
||||
monitored_outputs = outputs + switchable_outputs
|
||||
|
||||
self.controller = AsyncSatel(
|
||||
host, port, hass.loop, zones, monitored_outputs, partitions
|
||||
)
|
||||
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.close)
|
||||
)
|
||||
self.controller = AsyncSatel(host, port, zones, monitored_outputs, partitions)
|
||||
|
||||
async def async_connect(
|
||||
self,
|
||||
zones_update_callback: Callable[[dict[str, dict[int, int]]], None],
|
||||
outputs_update_callback: Callable[[dict[str, dict[int, int]]], None],
|
||||
zones_update_callback: Callable[[dict[int, int]], None],
|
||||
outputs_update_callback: Callable[[dict[int, int]], None],
|
||||
partitions_update_callback: Callable[[], None],
|
||||
) -> None:
|
||||
"""Start controller connection."""
|
||||
@@ -80,26 +74,15 @@ class SatelClient:
|
||||
if not result:
|
||||
raise ConfigEntryNotReady("Controller failed to connect")
|
||||
|
||||
self.config_entry.async_create_background_task(
|
||||
self.hass,
|
||||
self.controller.keep_alive(),
|
||||
f"satel_integra.{self.config_entry.entry_id}.keep_alive",
|
||||
eager_start=False,
|
||||
self.controller.register_callbacks(
|
||||
alarm_status_callback=partitions_update_callback,
|
||||
zone_changed_callback=zones_update_callback,
|
||||
output_changed_callback=outputs_update_callback,
|
||||
)
|
||||
|
||||
self.config_entry.async_create_background_task(
|
||||
self.hass,
|
||||
self.controller.monitor_status(
|
||||
partitions_update_callback,
|
||||
zones_update_callback,
|
||||
outputs_update_callback,
|
||||
),
|
||||
f"satel_integra.{self.config_entry.entry_id}.monitor_status",
|
||||
eager_start=False,
|
||||
)
|
||||
await self.controller.start(enable_monitoring=True)
|
||||
|
||||
@callback
|
||||
def close(self, *args, **kwargs) -> None:
|
||||
async def async_close(self) -> None:
|
||||
"""Close the connection."""
|
||||
|
||||
self.controller.close()
|
||||
await self.controller.close()
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from satel_integra.satel_integra import AsyncSatel
|
||||
from satel_integra import AsyncSatel
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
@@ -191,14 +191,19 @@ class SatelConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
async def test_connection(self, host: str, port: int) -> bool:
|
||||
"""Test a connection to the Satel alarm."""
|
||||
controller = AsyncSatel(host, port, self.hass.loop)
|
||||
controller = AsyncSatel(host, port)
|
||||
|
||||
result = await controller.connect()
|
||||
|
||||
# Make sure we close the connection again
|
||||
controller.close()
|
||||
|
||||
return result
|
||||
try:
|
||||
return await controller.connect(check_busy=False)
|
||||
except Exception:
|
||||
_LOGGER.exception(
|
||||
"Unexpected error during connection test to %s:%s",
|
||||
host,
|
||||
port,
|
||||
)
|
||||
return False
|
||||
finally:
|
||||
await controller.close()
|
||||
|
||||
|
||||
class SatelOptionsFlow(OptionsFlow):
|
||||
|
||||
@@ -17,5 +17,3 @@ CONF_SWITCHABLE_OUTPUT_NUMBER = "switchable_output_number"
|
||||
|
||||
CONF_ARM_HOME_MODE = "arm_home_mode"
|
||||
CONF_ZONE_TYPE = "type"
|
||||
|
||||
ZONES = "zones"
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
|
||||
from satel_integra.satel_integra import AlarmState
|
||||
from satel_integra import AlarmState
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -13,7 +13,6 @@ from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .client import SatelClient
|
||||
from .const import ZONES
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -64,11 +63,11 @@ class SatelIntegraZonesCoordinator(SatelIntegraBaseCoordinator[dict[int, bool]])
|
||||
self.data = {}
|
||||
|
||||
@callback
|
||||
def zones_update_callback(self, status: dict[str, dict[int, int]]) -> None:
|
||||
def zones_update_callback(self, status: dict[int, int]) -> None:
|
||||
"""Update zone objects as per notification from the alarm."""
|
||||
_LOGGER.debug("Zones callback, status: %s", status)
|
||||
|
||||
update_data = {zone: value == 1 for zone, value in status[ZONES].items()}
|
||||
update_data = {zone: value == 1 for zone, value in status.items()}
|
||||
|
||||
self.async_set_updated_data(update_data)
|
||||
|
||||
@@ -85,13 +84,11 @@ class SatelIntegraOutputsCoordinator(SatelIntegraBaseCoordinator[dict[int, bool]
|
||||
self.data = {}
|
||||
|
||||
@callback
|
||||
def outputs_update_callback(self, status: dict[str, dict[int, int]]) -> None:
|
||||
def outputs_update_callback(self, status: dict[int, int]) -> None:
|
||||
"""Update output objects as per notification from the alarm."""
|
||||
_LOGGER.debug("Outputs callback, status: %s", status)
|
||||
|
||||
update_data = {
|
||||
output: value == 1 for output, value in status["outputs"].items()
|
||||
}
|
||||
update_data = {output: value == 1 for output, value in status.items()}
|
||||
|
||||
self.async_set_updated_data(update_data)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from satel_integra.satel_integra import AsyncSatel
|
||||
from satel_integra import AsyncSatel
|
||||
|
||||
from homeassistant.config_entries import ConfigSubentry
|
||||
from homeassistant.const import CONF_NAME
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["satel_integra"],
|
||||
"requirements": ["satel-integra==0.3.7"]
|
||||
"requirements": ["satel-integra==1.0.0"]
|
||||
}
|
||||
|
||||
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@@ -2886,7 +2886,7 @@ samsungtvws[async,encrypted]==2.7.2
|
||||
sanix==1.0.6
|
||||
|
||||
# homeassistant.components.satel_integra
|
||||
satel-integra==0.3.7
|
||||
satel-integra==1.0.0
|
||||
|
||||
# homeassistant.components.screenlogic
|
||||
screenlogicpy==0.10.2
|
||||
|
||||
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@@ -2446,7 +2446,7 @@ samsungtvws[async,encrypted]==2.7.2
|
||||
sanix==1.0.6
|
||||
|
||||
# homeassistant.components.satel_integra
|
||||
satel-integra==0.3.7
|
||||
satel-integra==1.0.0
|
||||
|
||||
# homeassistant.components.screenlogic
|
||||
screenlogicpy==0.10.2
|
||||
|
||||
@@ -94,13 +94,19 @@ def get_monitor_callbacks(
|
||||
mock_satel: AsyncMock,
|
||||
) -> tuple[
|
||||
Callable[[], None],
|
||||
Callable[[dict[str, dict[int, int]]], None],
|
||||
Callable[[dict[str, dict[int, int]]], None],
|
||||
Callable[[dict[int, int]], None],
|
||||
Callable[[dict[int, int]], None],
|
||||
]:
|
||||
"""Return (partitions_cb, zones_cb, outputs_cb) passed to monitor_status."""
|
||||
if not mock_satel.monitor_status.call_args_list:
|
||||
pytest.fail("monitor_status was not called")
|
||||
"""Return callbacks passed to `register_callbacks`."""
|
||||
if not mock_satel.register_callbacks.call_args_list:
|
||||
pytest.fail("register_callbacks was not called")
|
||||
|
||||
call = mock_satel.register_callbacks.call_args_list[-1]
|
||||
if call.kwargs:
|
||||
partitions_cb = call.kwargs["alarm_status_callback"]
|
||||
zones_cb = call.kwargs["zone_changed_callback"]
|
||||
outputs_cb = call.kwargs["output_changed_callback"]
|
||||
else:
|
||||
partitions_cb, zones_cb, outputs_cb = call.args
|
||||
|
||||
call = mock_satel.monitor_status.call_args_list[-1]
|
||||
partitions_cb, zones_cb, outputs_cb = call.args
|
||||
return partitions_cb, zones_cb, outputs_cb
|
||||
|
||||
@@ -16,6 +16,7 @@ from . import (
|
||||
MOCK_PARTITION_SUBENTRY,
|
||||
MOCK_SWITCHABLE_OUTPUT_SUBENTRY,
|
||||
MOCK_ZONE_SUBENTRY,
|
||||
get_monitor_callbacks,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
@@ -63,13 +64,18 @@ def mock_satel() -> Generator[AsyncMock]:
|
||||
client.connect = AsyncMock(return_value=True)
|
||||
client.set_output = AsyncMock()
|
||||
|
||||
# Immediately push baseline values so entities have stable states for snapshots
|
||||
async def _monitor_status(partitions_cb, zones_cb, outputs_cb):
|
||||
partitions_cb()
|
||||
zones_cb({"zones": {1: 0}})
|
||||
outputs_cb({"outputs": {1: 0}})
|
||||
client.register_callbacks = MagicMock()
|
||||
|
||||
client.monitor_status = AsyncMock(side_effect=_monitor_status)
|
||||
# Immediately push baseline values so entities have stable states for snapshots
|
||||
async def _start(**_: object) -> None:
|
||||
alarm_status_callback, zone_changed_callback, output_changed_callback = (
|
||||
get_monitor_callbacks(client)
|
||||
)
|
||||
alarm_status_callback()
|
||||
zone_changed_callback({1: 0})
|
||||
output_changed_callback({1: 0})
|
||||
|
||||
client.start = AsyncMock(side_effect=_start)
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
@@ -81,13 +81,13 @@ async def test_binary_sensor_initial_state(
|
||||
"""Test binary sensors have a correct initial state after initialization."""
|
||||
|
||||
# Instantly call callback to ensure we have initial data set
|
||||
async def mock_monitor_callback(
|
||||
alarm_status_callback, zones_callback, outputs_callback
|
||||
):
|
||||
outputs_callback({"outputs": violated_entries})
|
||||
zones_callback({"zones": violated_entries})
|
||||
async def mock_start(**_: object) -> None:
|
||||
_, zones_callback, outputs_callback = get_monitor_callbacks(mock_satel)
|
||||
|
||||
mock_satel.monitor_status = AsyncMock(side_effect=mock_monitor_callback)
|
||||
outputs_callback(violated_entries)
|
||||
zones_callback(violated_entries)
|
||||
|
||||
mock_satel.start = AsyncMock(side_effect=mock_start)
|
||||
|
||||
await setup_integration(hass, mock_config_entry_with_subentries)
|
||||
|
||||
@@ -108,19 +108,19 @@ async def test_binary_sensor_callback(
|
||||
|
||||
_, zone_update_method, output_update_method = get_monitor_callbacks(mock_satel)
|
||||
|
||||
output_update_method({"outputs": {1: 1}})
|
||||
zone_update_method({"zones": {1: 1}})
|
||||
output_update_method({1: 1})
|
||||
zone_update_method({1: 1})
|
||||
assert hass.states.get("binary_sensor.zone").state == STATE_ON
|
||||
assert hass.states.get("binary_sensor.output").state == STATE_ON
|
||||
|
||||
output_update_method({"outputs": {1: 0}})
|
||||
zone_update_method({"zones": {1: 0}})
|
||||
output_update_method({1: 0})
|
||||
zone_update_method({1: 0})
|
||||
assert hass.states.get("binary_sensor.zone").state == STATE_OFF
|
||||
assert hass.states.get("binary_sensor.output").state == STATE_OFF
|
||||
|
||||
# The client library should always report all entries, but test that we set the status correctly if it doesn't
|
||||
output_update_method({"outputs": {2: 1}})
|
||||
zone_update_method({"zones": {2: 1}})
|
||||
output_update_method({2: 1})
|
||||
zone_update_method({2: 1})
|
||||
assert hass.states.get("binary_sensor.zone").state == STATE_UNKNOWN
|
||||
assert hass.states.get("binary_sensor.output").state == STATE_UNKNOWN
|
||||
|
||||
@@ -145,8 +145,8 @@ async def test_binary_sensor_last_reported(
|
||||
|
||||
# Run callbacks with same payload
|
||||
_, zone_update_method, output_update_method = get_monitor_callbacks(mock_satel)
|
||||
output_update_method({"outputs": {1: 0}})
|
||||
zone_update_method({"zones": {1: 0}})
|
||||
output_update_method({1: 0})
|
||||
zone_update_method({1: 0})
|
||||
|
||||
assert first_reported != hass.states.get("binary_sensor.zone").last_reported
|
||||
assert len(events) == 2 # last_reported shall not fire state_changed
|
||||
|
||||
@@ -84,12 +84,12 @@ async def test_switch_initial_state(
|
||||
"""Test switch has a correct initial state after initialization."""
|
||||
|
||||
# Instantly call callback to ensure we have initial data set
|
||||
async def mock_monitor_callback(
|
||||
alarm_status_callback, zones_callback, outputs_callback
|
||||
):
|
||||
outputs_callback({"outputs": violated_outputs})
|
||||
async def mock_start(**_: object) -> None:
|
||||
_, _, outputs_callback = get_monitor_callbacks(mock_satel)
|
||||
|
||||
mock_satel.monitor_status = AsyncMock(side_effect=mock_monitor_callback)
|
||||
outputs_callback(violated_outputs)
|
||||
|
||||
mock_satel.start = AsyncMock(side_effect=mock_start)
|
||||
|
||||
await setup_integration(hass, mock_config_entry_with_subentries)
|
||||
|
||||
@@ -108,14 +108,14 @@ async def test_switch_callback(
|
||||
|
||||
_, _, output_update_method = get_monitor_callbacks(mock_satel)
|
||||
|
||||
output_update_method({"outputs": {1: 1}})
|
||||
output_update_method({1: 1})
|
||||
assert hass.states.get("switch.switchable_output").state == STATE_ON
|
||||
|
||||
output_update_method({"outputs": {1: 0}})
|
||||
output_update_method({1: 0})
|
||||
assert hass.states.get("switch.switchable_output").state == STATE_OFF
|
||||
|
||||
# The client library should always report all entries, but test that we set the status correctly if it doesn't
|
||||
output_update_method({"outputs": {2: 1}})
|
||||
output_update_method({2: 1})
|
||||
assert hass.states.get("switch.switchable_output").state == STATE_UNKNOWN
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ async def test_switch_last_reported(
|
||||
|
||||
# Run callbacks with same payload
|
||||
_, _, output_update_method = get_monitor_callbacks(mock_satel)
|
||||
output_update_method({"outputs": {1: 0}})
|
||||
output_update_method({1: 0})
|
||||
|
||||
assert first_reported != hass.states.get("switch.switchable_output").last_reported
|
||||
assert len(events) == 1 # last_reported shall not fire state_changed
|
||||
|
||||
Reference in New Issue
Block a user