async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Elexa Guardian from a config entry.""" hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = { DATA_COORDINATOR: {}, DATA_COORDINATOR_PAIRED_SENSOR: {}, } client = Client(entry.data[CONF_IP_ADDRESS], port=entry.data[CONF_PORT]) # The valve controller's UDP-based API can't handle concurrent requests very well, # so we use a lock to ensure that only one API request is reaching it at a time: api_lock = asyncio.Lock() # Set up DataUpdateCoordinators for the valve controller: init_valve_controller_tasks = [] for api, api_coro in ( (API_SENSOR_PAIR_DUMP, client.sensor.pair_dump), (API_SYSTEM_DIAGNOSTICS, client.system.diagnostics), (API_SYSTEM_ONBOARD_SENSOR_STATUS, client.system.onboard_sensor_status), (API_VALVE_STATUS, client.valve.status), (API_WIFI_STATUS, client.wifi.status), ): coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR][ api ] = GuardianDataUpdateCoordinator( hass, client=client, api_name=api, api_coro=api_coro, api_lock=api_lock, valve_controller_uid=entry.data[CONF_UID], ) init_valve_controller_tasks.append(coordinator.async_refresh()) await asyncio.gather(*init_valve_controller_tasks) hass.data[DOMAIN][entry.entry_id][DATA_CLIENT] = client # Set up an object to evaluate each batch of paired sensor UIDs and add/remove # devices as appropriate: paired_sensor_manager = hass.data[DOMAIN][entry.entry_id][ DATA_PAIRED_SENSOR_MANAGER ] = PairedSensorManager(hass, entry, client, api_lock) await paired_sensor_manager.async_process_latest_paired_sensor_uids() @callback def async_process_paired_sensor_uids() -> None: """Define a callback for when new paired sensor data is received.""" hass.async_create_task( paired_sensor_manager.async_process_latest_paired_sensor_uids() ) hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR][ API_SENSOR_PAIR_DUMP ].async_add_listener(async_process_paired_sensor_uids) # Set up all of the Guardian entity platforms: hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def main() -> None: """Create the aiohttp session and run the example.""" logging.basicConfig(level=logging.INFO) async with Client("172.16.11.208") as guardian: try: diagnostics_response = await guardian.system.diagnostics() _LOGGER.info("diagnostics command response: %s", diagnostics_response) onboard_sensor_status = await guardian.system.onboard_sensor_status( ) _LOGGER.info("onboard_sensor_status command response: %s", onboard_sensor_status) ping_response = await guardian.system.ping() _LOGGER.info("ping command response: %s", ping_response) # reboot_response = await guardian.system.reboot() # _LOGGER.info("reboot command response: %s", reboot_response) # factory_reset_response = await guardian.system.factory_reset() # _LOGGER.info("factory_reset command response: %s", factory_reset_response) # upgrade_firmware_response = await guardian.system.upgrade_firmware() # _LOGGER.info( # "upgrade_firmware command response: %s", upgrade_firmware_response # ) except GuardianError as err: _LOGGER.info(err)
async def test_command_without_socket_connect(): """Test that executing a command without an open connection throws an exception.""" client = Client("192.168.1.100") with pytest.raises(SocketError) as err: await client.system.ping() assert str(err.value) == "You aren't connected to the device yet"
async def main() -> None: """Create the aiohttp session and run the example.""" logging.basicConfig(level=logging.INFO) async with Client("172.16.11.208") as guardian: try: wifi_status_response = await guardian.wifi.status() _LOGGER.info("wifi_status command response: %s", wifi_status_response) # wifi_reset_response = await guardian.wifi.reset() # _LOGGER.info("wifi_reset command response: %s", wifi_reset_response) # wifi_configure_response = await guardian.wifi.configure( # "<SSID>", "<PASSWORD>" # ) # _LOGGER.info("wifi_configure command response: %s", wifi_configure_response) # wifi_enable_ap_response = await guardian.wifi.enable_ap() # _LOGGER.info("wifi_enable_ap command response: %s", wifi_enable_ap_response) # wifi_disable_ap_response = await guardian.wifi.disable_ap() # _LOGGER.info( # "wifi_disable_ap command response: %s", wifi_disable_ap_response # ) except GuardianError as err: _LOGGER.info(err)
async def test_close_success(mock_datagram_client): """Test the valve_close command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: valve_close_response = await client.valve.close() assert valve_close_response["command"] == 18
async def main() -> None: """Create the aiohttp session and run the example.""" logging.basicConfig(level=logging.INFO) async with Client("172.16.11.208") as guardian: try: pair_dump_response = await guardian.sensor.pair_dump() _LOGGER.info("pair_dump_response command response: %s", pair_dump_response) for uid in pair_dump_response["data"]["paired_uids"]: paired_sensor_status_resp = await guardian.sensor.paired_sensor_status( uid) _LOGGER.info( "paired_sensor_status command response (UID: %s): %s", uid, paired_sensor_status_resp, ) # pair_sensor_response = await guardian.sensor.pair_sensor("<UID>") # _LOGGER.info("pair_response command response: %s", pair_sensor_response) # unpair_sensor_response = await guardian.sensor.unpair_sensor("<UID>") # _LOGGER.info("unpair_response command response: %s", unpair_sensor_response) except GuardianError as err: _LOGGER.info(err)
async def test_halt_failure(mock_datagram_client): """Test the valve_halt command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.valve.halt() assert str(err.value) == "valve_halt command failed: valve_already_stopped"
async def test_publish_state_success(mock_datagram_client): """Test the publish_state command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: publish_state_response = await client.iot.publish_state() assert publish_state_response["command"] == 65 assert publish_state_response["status"] == "ok"
async def test_open_failure(mock_datagram_client): """Test the valve_open command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.valve.open() assert str(err.value) == "valve_open command failed: valve_moving"
async def test_upgrade_firmware_success(mock_datagram_client): """Test the upgrade_firmware command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: upgrade_firmware_response = await client.system.upgrade_firmware() assert upgrade_firmware_response["command"] == 4 assert upgrade_firmware_response["status"] == "ok"
async def test_command_timeout(mock_datagram_client): """Test that a timeout during command execution throws an exception.""" with mock_datagram_client, patch("asyncio.sleep"): with pytest.raises(SocketError) as err: async with Client("192.168.1.100") as client: await client.system.ping() assert str(err.value) == "system_ping command timed out"
async def test_pair_sensor_success(mock_datagram_client): """Test the pair_sensor command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: pair_sensor_response = await client.sensor.pair_sensor("abc123") assert pair_sensor_response["command"] == 49 assert pair_sensor_response["status"] == "ok"
async def test_reset_success(mock_datagram_client): """Test the wifi_reset command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: wifi_reset_response = await client.wifi.reset() assert wifi_reset_response["command"] == 33 assert wifi_reset_response["status"] == "ok"
async def test_factory_reset_success(mock_datagram_client): """Test the factory_reset command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: factory_reset_response = await client.system.factory_reset() assert factory_reset_response["command"] == 255 assert factory_reset_response["status"] == "ok"
async def test_connect_timeout(): """Test that a timeout during connection throws an exception.""" with patch("asyncio_dgram.connect", AsyncMock(side_effect=asyncio.TimeoutError)): with pytest.raises(SocketError) as err: async with Client("192.168.1.100") as client: await client.system.ping() assert str(err.value) == "Connection to device timed out"
async def test_upgrade_firmware_custom_parameters(mock_datagram_client): """Test that valid custom parameters work.""" with mock_datagram_client: async with Client("192.168.1.100") as client: upgrade_firmware_response = await client.system.upgrade_firmware( url="https://firmware.com", port=8080, filename="123.bin") assert upgrade_firmware_response["command"] == 4 assert upgrade_firmware_response["status"] == "ok"
async def test_configure_success(mock_datagram_client): """Test the wifi_configure command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: wifi_configure_response = await client.wifi.configure( "My_Network", "password123") assert wifi_configure_response["command"] == 34 assert wifi_configure_response["status"] == "ok"
async def test_raw_command_success(mock_datagram_client): """Test a successful raw command.""" with mock_datagram_client: async with Client("192.168.1.100") as client: ping_response = await client.execute_raw_command(0) assert ping_response["command"] == 0 assert ping_response["status"] == "ok" assert ping_response["data"]["uid"] == "ABCDEF123456"
async def test_reset_failure(mock_datagram_client): """Test the valve_reset command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.valve.reset() assert (str(err.value) == "valve_reset command failed " "(response: {'command': 20, 'status': 'error'})")
async def test_ping_success(mock_datagram_client): """Test the ping command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: ping_response = await client.system.ping() assert ping_response["command"] == 0 assert ping_response["status"] == "ok" assert ping_response["data"] == {"uid": "ABCDEF123456"}
async def test_ping_failure(mock_datagram_client): """Test a failured ping of the device.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.system.ping() assert str(err.value) == ( "system_ping command failed (response: {'command': 0, 'status': 'error'})" )
async def test_upgrade_firmware_invalid_url(mock_datagram_client): """Test that an invalid firmware URL throws an exception.""" with mock_datagram_client: with pytest.raises(GuardianError) as err: async with Client("192.168.1.100") as client: _ = await client.system.upgrade_firmware(url="not_real_url") assert str(err.value) == ( "Invalid parameters provided: Invalid URL for dictionary value @ " "data['url']")
async def test_onboard_sensor_status_failure(mock_datagram_client): """Test the onboard_sensor_status command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.system.onboard_sensor_status() assert str( err.value) == ("system_onboard_sensor_status command failed " "(response: {'command': 80, 'status': 'error'})")
async def test_paired_sensor_status_failure_not_paired(mock_datagram_client): """Test the paired_sensor_status command failing because it isn't paired.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.sensor.paired_sensor_status("ABCDE1234567") assert (str( err.value ) == "sensor_paired_sensor_status command failed: sensor_not_paired")
async def test_configure_failure(mock_datagram_client): """Test the wifi_configure command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.wifi.configure("My_Network", "password123") assert str( err.value) == ("wifi_configure command failed " "(response: {'command': 34, 'status': 'error'})")
async def test_paired_sensor_status_invalid_uid(mock_datagram_client): """Test that an invalid UID throws an exception.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.sensor.paired_sensor_status("$@&*!@--") assert str(err.value) == ( "Invalid parameters provided: String is not alphanumeric for dictionary " "value @ data['uid']")
async def test_reboot_failure(mock_datagram_client): """Test the reboot command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.system.reboot() assert str(err.value) == ( "system_reboot command failed (response: {'command': 2, 'status': 'error'})" )
async def test_publish_state_failure(mock_datagram_client): """Test the publish_state command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.iot.publish_state() client.disconnect() assert str(err.value) == ("iot_publish_state command failed " "(response: {'command': 65, 'status': 'error'})")
async def test_reboot_success(mock_datagram_client): """Test the reboot command succeeding.""" with mock_datagram_client: async with Client("192.168.1.100") as client: # Patch asyncio.sleep so that this test doesn't take 3-ish seconds: with patch("asyncio.sleep", AsyncMock()): reboot_response = await client.system.reboot() assert reboot_response["command"] == 2 assert reboot_response["status"] == "ok"
async def test_status_failure(mock_datagram_client): """Test the wifi_status command failing.""" with mock_datagram_client: with pytest.raises(CommandError) as err: async with Client("192.168.1.100") as client: _ = await client.wifi.status() assert str(err.value) == ( "wifi_status command failed (response: {'command': 32, 'status': 'error'})" )