Add screenlogic service tests (#116356)

This commit is contained in:
Kevin Worrel 2024-05-09 05:19:58 -07:00 committed by GitHub
parent b30a02dee6
commit c1f0ebee2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 1419 additions and 3 deletions

View file

@ -1203,7 +1203,6 @@ omit =
homeassistant/components/screenlogic/light.py
homeassistant/components/screenlogic/number.py
homeassistant/components/screenlogic/sensor.py
homeassistant/components/screenlogic/services.py
homeassistant/components/screenlogic/switch.py
homeassistant/components/scsgate/*
homeassistant/components/sendgrid/notify.py

View file

@ -10,9 +10,13 @@ MOCK_ADAPTER_MAC = "aa:bb:cc:dd:ee:ff"
MOCK_ADAPTER_IP = "127.0.0.1"
MOCK_ADAPTER_PORT = 80
MOCK_CONFIG_ENTRY_ID = "screenlogictest"
MOCK_DEVICE_AREA = "pool"
_LOGGER = logging.getLogger(__name__)
GATEWAY_IMPORT_PATH = "homeassistant.components.screenlogic.ScreenLogicGateway"
GATEWAY_DISCOVERY_IMPORT_PATH = "homeassistant.components.screenlogic.coordinator.async_discover_gateways_by_unique_id"
@ -36,6 +40,9 @@ def num_key_string_to_int(data: dict) -> None:
DATA_FULL_CHEM = num_key_string_to_int(
load_json_object_fixture("screenlogic/data_full_chem.json")
)
DATA_FULL_CHEM_CHLOR = num_key_string_to_int(
load_json_object_fixture("screenlogic/data_full_chem_chlor.json")
)
DATA_FULL_NO_GPM = num_key_string_to_int(
load_json_object_fixture("screenlogic/data_full_no_gpm.json")
)

View file

@ -5,7 +5,13 @@ import pytest
from homeassistant.components.screenlogic import DOMAIN
from homeassistant.const import CONF_IP_ADDRESS, CONF_PORT, CONF_SCAN_INTERVAL
from . import MOCK_ADAPTER_IP, MOCK_ADAPTER_MAC, MOCK_ADAPTER_NAME, MOCK_ADAPTER_PORT
from . import (
MOCK_ADAPTER_IP,
MOCK_ADAPTER_MAC,
MOCK_ADAPTER_NAME,
MOCK_ADAPTER_PORT,
MOCK_CONFIG_ENTRY_ID,
)
from tests.common import MockConfigEntry
@ -24,5 +30,5 @@ def mock_config_entry() -> MockConfigEntry:
CONF_SCAN_INTERVAL: 30,
},
unique_id=MOCK_ADAPTER_MAC,
entry_id="screenlogictest",
entry_id=MOCK_CONFIG_ENTRY_ID,
)

View file

@ -0,0 +1,909 @@
{
"adapter": {
"firmware": {
"name": "Protocol Adapter Firmware",
"value": "POOL: 5.2 Build 736.0 Rel",
"major": 5.2,
"minor": 736.0
}
},
"controller": {
"controller_id": 100,
"configuration": {
"body_type": {
"0": {
"min_setpoint": 40,
"max_setpoint": 104
},
"1": {
"min_setpoint": 40,
"max_setpoint": 104
}
},
"is_celsius": {
"name": "Is Celsius",
"value": 0
},
"controller_type": 13,
"hardware_type": 0,
"controller_data": 0,
"generic_circuit_name": "Water Features",
"circuit_count": 11,
"color_count": 8,
"color": [
{
"name": "White",
"value": [255, 255, 255]
},
{
"name": "Light Green",
"value": [160, 255, 160]
},
{
"name": "Green",
"value": [0, 255, 80]
},
{
"name": "Cyan",
"value": [0, 255, 200]
},
{
"name": "Blue",
"value": [100, 140, 255]
},
{
"name": "Lavender",
"value": [230, 130, 255]
},
{
"name": "Magenta",
"value": [255, 0, 128]
},
{
"name": "Light Magenta",
"value": [255, 180, 210]
}
],
"interface_tab_flags": 127,
"show_alarms": 0,
"remotes": 0,
"unknown_at_offset_09": 0,
"unknown_at_offset_10": 0,
"unknown_at_offset_11": 0
},
"model": {
"name": "Model",
"value": "EasyTouch2 8"
},
"equipment": {
"flags": 98364,
"list": [
"CHLORINATOR",
"INTELLIBRITE",
"INTELLIFLO_0",
"INTELLIFLO_1",
"INTELLICHEM",
"HYBRID_HEATER"
]
},
"sensor": {
"state": {
"name": "Controller State",
"value": 1,
"device_type": "enum",
"enum_options": ["Unknown", "Ready", "Sync", "Service"]
},
"freeze_mode": {
"name": "Freeze Mode",
"value": 0
},
"pool_delay": {
"name": "Pool Delay",
"value": 0
},
"spa_delay": {
"name": "Spa Delay",
"value": 0
},
"cleaner_delay": {
"name": "Cleaner Delay",
"value": 0
},
"air_temperature": {
"name": "Air Temperature",
"value": 69,
"unit": "\u00b0F",
"device_type": "temperature",
"state_type": "measurement"
},
"ph": {
"name": "pH",
"value": 7.61,
"unit": "pH",
"state_type": "measurement"
},
"orp": {
"name": "ORP",
"value": 728,
"unit": "mV",
"state_type": "measurement"
},
"saturation": {
"name": "Saturation Index",
"value": 0.06,
"unit": "lsi",
"state_type": "measurement"
},
"salt_ppm": {
"name": "Salt",
"value": 0,
"unit": "ppm",
"state_type": "measurement"
},
"ph_supply_level": {
"name": "pH Supply Level",
"value": 2,
"state_type": "measurement"
},
"orp_supply_level": {
"name": "ORP Supply Level",
"value": 3,
"state_type": "measurement"
},
"active_alert": {
"name": "Active Alert",
"value": 0,
"device_type": "alarm"
}
},
"date_time": {
"timestamp": 1700489169.0,
"timestamp_host": 1700517812.0,
"auto_dst": {
"name": "Automatic Daylight Saving Time",
"value": 1
}
}
},
"circuit": {
"500": {
"circuit_id": 500,
"name": "Spa",
"configuration": {
"name_index": 71,
"flags": 1,
"default_runtime": 720,
"unknown_at_offset_62": 0,
"unknown_at_offset_63": 0,
"delay": 0
},
"function": 1,
"interface": 1,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 1,
"value": 0
},
"501": {
"circuit_id": 501,
"name": "Waterfall",
"configuration": {
"name_index": 85,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_94": 0,
"unknown_at_offset_95": 0,
"delay": 0
},
"function": 0,
"interface": 2,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 2,
"value": 0
},
"502": {
"circuit_id": 502,
"name": "Pool Light",
"configuration": {
"name_index": 62,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_126": 0,
"unknown_at_offset_127": 0,
"delay": 0
},
"function": 16,
"interface": 3,
"color": {
"color_set": 2,
"color_position": 0,
"color_stagger": 2
},
"device_id": 3,
"value": 0
},
"503": {
"circuit_id": 503,
"name": "Spa Light",
"configuration": {
"name_index": 73,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_158": 0,
"unknown_at_offset_159": 0,
"delay": 0
},
"function": 16,
"interface": 3,
"color": {
"color_set": 6,
"color_position": 1,
"color_stagger": 10
},
"device_id": 4,
"value": 0
},
"504": {
"circuit_id": 504,
"name": "Cleaner",
"configuration": {
"name_index": 21,
"flags": 0,
"default_runtime": 240,
"unknown_at_offset_186": 0,
"unknown_at_offset_187": 0,
"delay": 0
},
"function": 5,
"interface": 0,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 5,
"value": 0
},
"505": {
"circuit_id": 505,
"name": "Pool Low",
"configuration": {
"name_index": 63,
"flags": 1,
"default_runtime": 720,
"unknown_at_offset_214": 0,
"unknown_at_offset_215": 0,
"delay": 0
},
"function": 2,
"interface": 0,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 6,
"value": 0
},
"506": {
"circuit_id": 506,
"name": "Yard Light",
"configuration": {
"name_index": 91,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_246": 0,
"unknown_at_offset_247": 0,
"delay": 0
},
"function": 7,
"interface": 4,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 7,
"value": 0
},
"507": {
"circuit_id": 507,
"name": "Cameras",
"configuration": {
"name_index": 101,
"flags": 0,
"default_runtime": 1620,
"unknown_at_offset_274": 0,
"unknown_at_offset_275": 0,
"delay": 0
},
"function": 0,
"interface": 2,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 8,
"value": 1
},
"508": {
"circuit_id": 508,
"name": "Pool High",
"configuration": {
"name_index": 61,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_306": 0,
"unknown_at_offset_307": 0,
"delay": 0
},
"function": 0,
"interface": 0,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 9,
"value": 0
},
"510": {
"circuit_id": 510,
"name": "Spillway",
"configuration": {
"name_index": 78,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_334": 0,
"unknown_at_offset_335": 0,
"delay": 0
},
"function": 14,
"interface": 1,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 11,
"value": 0
},
"511": {
"circuit_id": 511,
"name": "Pool High",
"configuration": {
"name_index": 61,
"flags": 0,
"default_runtime": 720,
"unknown_at_offset_366": 0,
"unknown_at_offset_367": 0,
"delay": 0
},
"function": 0,
"interface": 5,
"color": {
"color_set": 0,
"color_position": 0,
"color_stagger": 0
},
"device_id": 12,
"value": 0
}
},
"pump": {
"0": {
"data": 70,
"type": 3,
"state": {
"name": "Pool Low Pump",
"value": 0
},
"watts_now": {
"name": "Pool Low Pump Watts Now",
"value": 0,
"unit": "W",
"device_type": "power",
"state_type": "measurement"
},
"rpm_now": {
"name": "Pool Low Pump RPM Now",
"value": 0,
"unit": "rpm",
"state_type": "measurement"
},
"unknown_at_offset_16": 0,
"gpm_now": {
"name": "Pool Low Pump GPM Now",
"value": 0,
"unit": "gpm",
"state_type": "measurement"
},
"unknown_at_offset_24": 255,
"preset": {
"0": {
"device_id": 6,
"setpoint": 63,
"is_rpm": 0
},
"1": {
"device_id": 9,
"setpoint": 72,
"is_rpm": 0
},
"2": {
"device_id": 1,
"setpoint": 3450,
"is_rpm": 1
},
"3": {
"device_id": 130,
"setpoint": 75,
"is_rpm": 0
},
"4": {
"device_id": 12,
"setpoint": 72,
"is_rpm": 0
},
"5": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"6": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"7": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
}
}
},
"1": {
"data": 66,
"type": 3,
"state": {
"name": "Waterfall Pump",
"value": 0
},
"watts_now": {
"name": "Waterfall Pump Watts Now",
"value": 0,
"unit": "W",
"device_type": "power",
"state_type": "measurement"
},
"rpm_now": {
"name": "Waterfall Pump RPM Now",
"value": 0,
"unit": "rpm",
"state_type": "measurement"
},
"unknown_at_offset_16": 0,
"gpm_now": {
"name": "Waterfall Pump GPM Now",
"value": 0,
"unit": "gpm",
"state_type": "measurement"
},
"unknown_at_offset_24": 255,
"preset": {
"0": {
"device_id": 2,
"setpoint": 2700,
"is_rpm": 1
},
"1": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"2": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"3": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"4": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"5": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"6": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
},
"7": {
"device_id": 0,
"setpoint": 30,
"is_rpm": 0
}
}
},
"2": {
"data": 0
},
"3": {
"data": 0
},
"4": {
"data": 0
},
"5": {
"data": 0
},
"6": {
"data": 0
},
"7": {
"data": 0
}
},
"body": {
"0": {
"body_type": 0,
"min_setpoint": 40,
"max_setpoint": 104,
"name": "Pool",
"last_temperature": {
"name": "Last Pool Temperature",
"value": 81,
"unit": "\u00b0F",
"device_type": "temperature",
"state_type": "measurement"
},
"heat_state": {
"name": "Pool Heat",
"value": 0,
"device_type": "enum",
"enum_options": ["Off", "Solar", "Heater", "Both"]
},
"heat_setpoint": {
"name": "Pool Heat Set Point",
"value": 83,
"unit": "\u00b0F",
"device_type": "temperature"
},
"cool_setpoint": {
"name": "Pool Cool Set Point",
"value": 100,
"unit": "\u00b0F",
"device_type": "temperature"
},
"heat_mode": {
"name": "Pool Heat Mode",
"value": 0,
"device_type": "enum",
"enum_options": [
"Off",
"Solar",
"Solar Preferred",
"Heater",
"Don't Change"
]
}
},
"1": {
"body_type": 1,
"min_setpoint": 40,
"max_setpoint": 104,
"name": "Spa",
"last_temperature": {
"name": "Last Spa Temperature",
"value": 84,
"unit": "\u00b0F",
"device_type": "temperature",
"state_type": "measurement"
},
"heat_state": {
"name": "Spa Heat",
"value": 0,
"device_type": "enum",
"enum_options": ["Off", "Solar", "Heater", "Both"]
},
"heat_setpoint": {
"name": "Spa Heat Set Point",
"value": 94,
"unit": "\u00b0F",
"device_type": "temperature"
},
"cool_setpoint": {
"name": "Spa Cool Set Point",
"value": 69,
"unit": "\u00b0F",
"device_type": "temperature"
},
"heat_mode": {
"name": "Spa Heat Mode",
"value": 0,
"device_type": "enum",
"enum_options": [
"Off",
"Solar",
"Solar Preferred",
"Heater",
"Don't Change"
]
}
}
},
"intellichem": {
"unknown_at_offset_00": 42,
"unknown_at_offset_04": 0,
"sensor": {
"ph_now": {
"name": "pH Now",
"value": 0.0,
"unit": "pH",
"state_type": "measurement"
},
"orp_now": {
"name": "ORP Now",
"value": 0,
"unit": "mV",
"state_type": "measurement"
},
"ph_supply_level": {
"name": "pH Supply Level",
"value": 2,
"state_type": "measurement"
},
"orp_supply_level": {
"name": "ORP Supply Level",
"value": 3,
"state_type": "measurement"
},
"saturation": {
"name": "Saturation Index",
"value": 0.06,
"unit": "lsi",
"state_type": "measurement"
},
"ph_probe_water_temp": {
"name": "pH Probe Water Temperature",
"value": 81,
"unit": "\u00b0F",
"device_type": "temperature",
"state_type": "measurement"
}
},
"configuration": {
"ph_setpoint": {
"name": "pH Setpoint",
"value": 7.6,
"unit": "pH",
"max_setpoint": 7.6,
"min_setpoint": 7.2
},
"orp_setpoint": {
"name": "ORP Setpoint",
"value": 720,
"unit": "mV",
"max_setpoint": 800,
"min_setpoint": 400
},
"calcium_harness": {
"name": "Calcium Hardness",
"value": 800,
"unit": "ppm",
"max_setpoint": 800,
"min_setpoint": 25
},
"cya": {
"name": "Cyanuric Acid",
"value": 45,
"unit": "ppm",
"max_setpoint": 201,
"min_setpoint": 0
},
"total_alkalinity": {
"name": "Total Alkalinity",
"value": 45,
"unit": "ppm",
"max_setpoint": 800,
"min_setpoint": 25
},
"salt_tds_ppm": {
"name": "Salt/TDS",
"value": 1000,
"unit": "ppm",
"max_setpoint": 6500,
"min_setpoint": 500
},
"probe_is_celsius": 0,
"flags": 32
},
"dose_status": {
"ph_last_dose_time": {
"name": "Last pH Dose Time",
"value": 5,
"unit": "sec",
"device_type": "duration",
"state_type": "total_increasing"
},
"orp_last_dose_time": {
"name": "Last ORP Dose Time",
"value": 4,
"unit": "sec",
"device_type": "duration",
"state_type": "total_increasing"
},
"ph_last_dose_volume": {
"name": "Last pH Dose Volume",
"value": 8,
"unit": "mL",
"device_type": "volume",
"state_type": "total_increasing"
},
"orp_last_dose_volume": {
"name": "Last ORP Dose Volume",
"value": 8,
"unit": "mL",
"device_type": "volume",
"state_type": "total_increasing"
},
"flags": 149,
"ph_dosing_state": {
"name": "pH Dosing State",
"value": 1,
"device_type": "enum",
"enum_options": ["Dosing", "Mixing", "Monitoring"]
},
"orp_dosing_state": {
"name": "ORP Dosing State",
"value": 2,
"device_type": "enum",
"enum_options": ["Dosing", "Mixing", "Monitoring"]
}
},
"alarm": {
"flags": 1,
"flow_alarm": {
"name": "Flow Alarm",
"value": 1,
"device_type": "alarm"
},
"ph_high_alarm": {
"name": "pH HIGH Alarm",
"value": 0,
"device_type": "alarm"
},
"ph_low_alarm": {
"name": "pH LOW Alarm",
"value": 0,
"device_type": "alarm"
},
"orp_high_alarm": {
"name": "ORP HIGH Alarm",
"value": 0,
"device_type": "alarm"
},
"orp_low_alarm": {
"name": "ORP LOW Alarm",
"value": 0,
"device_type": "alarm"
},
"ph_supply_alarm": {
"name": "pH Supply Alarm",
"value": 0,
"device_type": "alarm"
},
"orp_supply_alarm": {
"name": "ORP Supply Alarm",
"value": 0,
"device_type": "alarm"
},
"probe_fault_alarm": {
"name": "Probe Fault",
"value": 0,
"device_type": "alarm"
}
},
"alert": {
"flags": 0,
"ph_lockout": {
"name": "pH Lockout",
"value": 0
},
"ph_limit": {
"name": "pH Dose Limit Reached",
"value": 0
},
"orp_limit": {
"name": "ORP Dose Limit Reached",
"value": 0
}
},
"firmware": {
"name": "IntelliChem Firmware",
"value": "1.060",
"major": 1,
"minor": 60
},
"water_balance": {
"flags": 0,
"corrosive": {
"name": "SI Corrosive",
"value": 0,
"device_type": "alarm"
},
"scaling": {
"name": "SI Scaling",
"value": 0,
"device_type": "alarm"
}
},
"unknown_at_offset_44": 0,
"unknown_at_offset_45": 0,
"unknown_at_offset_46": 0
},
"scg": {
"scg_present": 1,
"sensor": {
"state": {
"name": "Chlorinator",
"value": 0
},
"salt_ppm": {
"name": "Chlorinator Salt",
"value": 0,
"unit": "ppm",
"state_type": "measurement"
}
},
"configuration": {
"pool_setpoint": {
"name": "Pool Chlorinator Setpoint",
"value": 51,
"unit": "%",
"min_setpoint": 0,
"max_setpoint": 100,
"step": 5,
"body_type": 0
},
"spa_setpoint": {
"name": "Spa Chlorinator Setpoint",
"value": 0,
"unit": "%",
"min_setpoint": 0,
"max_setpoint": 100,
"step": 5,
"body_type": 1
},
"super_chlor_timer": {
"name": "Super Chlorination Timer",
"value": 0,
"unit": "hr",
"min_setpoint": 1,
"max_setpoint": 72,
"step": 1
}
},
"flags": 0,
"super_chlorinate": {
"name": "Super Chlorinate",
"value": 0
}
}
}

View file

@ -0,0 +1,495 @@
"""Tests for ScreenLogic integration service calls."""
from typing import Any
from unittest.mock import DEFAULT, AsyncMock, patch
import pytest
from screenlogicpy import ScreenLogicGateway
from screenlogicpy.device_const.system import COLOR_MODE
from homeassistant.components.screenlogic import DOMAIN
from homeassistant.components.screenlogic.const import (
ATTR_COLOR_MODE,
ATTR_CONFIG_ENTRY,
ATTR_RUNTIME,
SERVICE_SET_COLOR_MODE,
SERVICE_START_SUPER_CHLORINATION,
SERVICE_STOP_SUPER_CHLORINATION,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import ATTR_AREA_ID, ATTR_DEVICE_ID, ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import device_registry as dr
from homeassistant.util import slugify
from . import (
DATA_FULL_CHEM,
DATA_FULL_CHEM_CHLOR,
DATA_MIN_ENTITY_CLEANUP,
GATEWAY_DISCOVERY_IMPORT_PATH,
MOCK_ADAPTER_MAC,
MOCK_ADAPTER_NAME,
MOCK_CONFIG_ENTRY_ID,
MOCK_DEVICE_AREA,
stub_async_connect,
)
from tests.common import MockConfigEntry
NON_SL_CONFIG_ENTRY_ID = "test"
@pytest.fixture(name="dataset")
def dataset_fixture():
"""Define the default dataset for service tests."""
return DATA_FULL_CHEM
@pytest.fixture(name="service_fixture")
async def setup_screenlogic_services_fixture(
hass: HomeAssistant,
request,
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
):
"""Define the setup for a patched screenlogic integration."""
data = (
marker.args[0]
if (marker := request.node.get_closest_marker("dataset")) is not None
else DATA_FULL_CHEM
)
def _service_connect(*args, **kwargs):
return stub_async_connect(data, *args, **kwargs)
mock_config_entry.add_to_hass(hass)
device: dr.DeviceEntry = device_registry.async_get_or_create(
config_entry_id=mock_config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, MOCK_ADAPTER_MAC)},
suggested_area=MOCK_DEVICE_AREA,
)
with (
patch(
GATEWAY_DISCOVERY_IMPORT_PATH,
return_value={},
),
patch.multiple(
ScreenLogicGateway,
async_connect=_service_connect,
is_connected=True,
_async_connected_request=DEFAULT,
async_set_color_lights=DEFAULT,
async_set_scg_config=DEFAULT,
) as gateway,
):
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
yield {"gateway": gateway, "device": device}
@pytest.mark.parametrize(
("data", "target"),
[
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_OFF.name.lower(),
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
},
None,
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower(),
},
{
ATTR_AREA_ID: MOCK_DEVICE_AREA,
},
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower(),
},
{
ATTR_ENTITY_ID: f"{Platform.SENSOR}.{slugify(f'{MOCK_ADAPTER_NAME} Air Temperature')}",
},
),
],
)
async def test_service_set_color_mode(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
) -> None:
"""Test set_color_mode service."""
mocked_async_set_color_lights: AsyncMock = service_fixture["gateway"][
"async_set_color_lights"
]
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
non_screenlogic_entry = MockConfigEntry(entry_id="test")
non_screenlogic_entry.add_to_hass(hass)
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COLOR_MODE,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_color_lights.assert_awaited_once()
async def test_service_set_color_mode_with_device(
hass: HomeAssistant,
service_fixture: dict[str, Any],
) -> None:
"""Test set_color_mode service with a device target."""
mocked_async_set_color_lights: AsyncMock = service_fixture["gateway"][
"async_set_color_lights"
]
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
sl_device: dr.DeviceEntry = service_fixture["device"]
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COLOR_MODE,
service_data={ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower()},
blocking=True,
target={ATTR_DEVICE_ID: sl_device.id},
)
mocked_async_set_color_lights.assert_awaited_once()
@pytest.mark.parametrize(
("data", "target", "error_msg"),
[
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_OFF.name.lower(),
ATTR_CONFIG_ENTRY: "invalidconfigentry",
},
None,
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry "
"'invalidconfigentry' not found",
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_OFF.name.lower(),
ATTR_CONFIG_ENTRY: NON_SL_CONFIG_ENTRY_ID,
},
None,
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry "
"'test' is not a screenlogic config",
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower(),
},
{
ATTR_AREA_ID: "invalidareaid",
},
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for "
"target not found",
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower(),
},
{
ATTR_DEVICE_ID: "invaliddeviceid",
},
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for "
"target not found",
),
(
{
ATTR_COLOR_MODE: COLOR_MODE.ALL_ON.name.lower(),
},
{
ATTR_ENTITY_ID: "sensor.invalidentityid",
},
f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for "
"target not found",
),
],
)
async def test_service_set_color_mode_error(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
error_msg: str,
) -> None:
"""Test set_color_mode service error cases."""
mocked_async_set_color_lights: AsyncMock = service_fixture["gateway"][
"async_set_color_lights"
]
non_screenlogic_entry = MockConfigEntry(entry_id=NON_SL_CONFIG_ENTRY_ID)
non_screenlogic_entry.add_to_hass(hass)
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
with pytest.raises(
ServiceValidationError,
match=error_msg,
):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COLOR_MODE,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_color_lights.assert_not_awaited()
@pytest.mark.dataset(DATA_FULL_CHEM_CHLOR)
@pytest.mark.parametrize(
("data", "target"),
[
(
{
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
ATTR_RUNTIME: 24,
},
None,
),
],
)
async def test_service_start_super_chlorination(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
) -> None:
"""Test start_super_chlorination service."""
mocked_async_set_scg_config: AsyncMock = service_fixture["gateway"][
"async_set_scg_config"
]
assert hass.services.has_service(DOMAIN, SERVICE_START_SUPER_CHLORINATION)
await hass.services.async_call(
DOMAIN,
SERVICE_START_SUPER_CHLORINATION,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_scg_config.assert_awaited_once()
@pytest.mark.parametrize(
("data", "target", "error_msg"),
[
(
{
ATTR_CONFIG_ENTRY: "invalidconfigentry",
ATTR_RUNTIME: 24,
},
None,
f"Failed to call service '{SERVICE_START_SUPER_CHLORINATION}'. "
"Config entry 'invalidconfigentry' not found",
),
(
{
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
ATTR_RUNTIME: 24,
},
None,
f"Equipment configuration for {MOCK_ADAPTER_NAME} does not"
f" support {SERVICE_START_SUPER_CHLORINATION}",
),
],
)
async def test_service_start_super_chlorination_error(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
error_msg: str,
) -> None:
"""Test start_super_chlorination service error cases."""
mocked_async_set_scg_config: AsyncMock = service_fixture["gateway"][
"async_set_scg_config"
]
assert hass.services.has_service(DOMAIN, SERVICE_START_SUPER_CHLORINATION)
with pytest.raises(
ServiceValidationError,
match=error_msg,
):
await hass.services.async_call(
DOMAIN,
SERVICE_START_SUPER_CHLORINATION,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_scg_config.assert_not_awaited()
@pytest.mark.dataset(DATA_FULL_CHEM_CHLOR)
@pytest.mark.parametrize(
("data", "target"),
[
(
{
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
},
None,
),
],
)
async def test_service_stop_super_chlorination(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
) -> None:
"""Test stop_super_chlorination service."""
mocked_async_set_scg_config: AsyncMock = service_fixture["gateway"][
"async_set_scg_config"
]
assert hass.services.has_service(DOMAIN, SERVICE_STOP_SUPER_CHLORINATION)
await hass.services.async_call(
DOMAIN,
SERVICE_STOP_SUPER_CHLORINATION,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_scg_config.assert_awaited_once()
@pytest.mark.parametrize(
("data", "target", "error_msg"),
[
(
{
ATTR_CONFIG_ENTRY: "invalidconfigentry",
},
None,
f"Failed to call service '{SERVICE_STOP_SUPER_CHLORINATION}'. "
"Config entry 'invalidconfigentry' not found",
),
(
{
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
},
None,
f"Equipment configuration for {MOCK_ADAPTER_NAME} does not"
f" support {SERVICE_STOP_SUPER_CHLORINATION}",
),
],
)
async def test_service_stop_super_chlorination_error(
hass: HomeAssistant,
service_fixture: dict[str, Any],
data: dict[str, Any],
target: dict[str, Any],
error_msg: str,
) -> None:
"""Test stop_super_chlorination service error cases."""
mocked_async_set_scg_config: AsyncMock = service_fixture["gateway"][
"async_set_scg_config"
]
assert hass.services.has_service(DOMAIN, SERVICE_STOP_SUPER_CHLORINATION)
with pytest.raises(
ServiceValidationError,
match=error_msg,
):
await hass.services.async_call(
DOMAIN,
SERVICE_STOP_SUPER_CHLORINATION,
service_data=data,
blocking=True,
target=target,
)
mocked_async_set_scg_config.assert_not_awaited()
async def test_service_config_entry_not_loaded(
hass: HomeAssistant,
device_registry: dr.DeviceRegistry,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the error case of config not loaded."""
mock_config_entry.add_to_hass(hass)
_ = device_registry.async_get_or_create(
config_entry_id=mock_config_entry.entry_id,
connections={(dr.CONNECTION_NETWORK_MAC, MOCK_ADAPTER_MAC)},
)
mock_set_color_lights = AsyncMock()
with (
patch(
GATEWAY_DISCOVERY_IMPORT_PATH,
return_value={},
),
patch.multiple(
ScreenLogicGateway,
async_connect=lambda *args, **kwargs: stub_async_connect(
DATA_MIN_ENTITY_CLEANUP, *args, **kwargs
),
async_disconnect=DEFAULT,
is_connected=True,
_async_connected_request=DEFAULT,
async_set_color_lights=mock_set_color_lights,
),
):
assert await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert hass.services.has_service(DOMAIN, SERVICE_SET_COLOR_MODE)
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
await mock_config_entry.async_unload(hass)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
with pytest.raises(
ServiceValidationError,
match=f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. "
f"Config entry '{MOCK_CONFIG_ENTRY_ID}' not loaded",
):
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COLOR_MODE,
service_data={
ATTR_COLOR_MODE: COLOR_MODE.ALL_OFF.name.lower(),
ATTR_CONFIG_ENTRY: MOCK_CONFIG_ENTRY_ID,
},
blocking=True,
)
mock_set_color_lights.assert_not_awaited()