|
36 | 36 | pw_circle = importlib.import_module("plugwise_usb.nodes.circle") |
37 | 37 | pw_sed = importlib.import_module("plugwise_usb.nodes.sed") |
38 | 38 | pw_scan = importlib.import_module("plugwise_usb.nodes.scan") |
| 39 | +pw_sense = importlib.import_module("plugwise_usb.nodes.sense") |
39 | 40 | pw_switch = importlib.import_module("plugwise_usb.nodes.switch") |
40 | 41 | pw_energy_counter = importlib.import_module("plugwise_usb.nodes.helpers.counter") |
41 | 42 | pw_energy_calibration = importlib.import_module("plugwise_usb.nodes.helpers") |
@@ -2312,6 +2313,255 @@ async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ign |
2312 | 2313 | ) |
2313 | 2314 | assert not state[pw_api.NodeFeature.AVAILABLE].state |
2314 | 2315 |
|
| 2316 | + @pytest.mark.asyncio |
| 2317 | + async def test_sense_node(self, monkeypatch: pytest.MonkeyPatch) -> None: # noqa: PLR0915 |
| 2318 | + """Testing properties of sense.""" |
| 2319 | + |
| 2320 | + def fake_cache(dummy: object, setting: str) -> str | None: # noqa: PLR0911 PLR0912 |
| 2321 | + """Fake cache retrieval.""" |
| 2322 | + if setting == pw_node.CACHE_FIRMWARE: |
| 2323 | + return "2011-11-3-13-7-33" |
| 2324 | + if setting == pw_node.CACHE_HARDWARE: |
| 2325 | + return "070030" |
| 2326 | + if setting == pw_node.CACHE_RELAY: |
| 2327 | + return "True" |
| 2328 | + if setting == pw_node.CACHE_NODE_INFO_TIMESTAMP: |
| 2329 | + return "2024-12-7-1-0-0" |
| 2330 | + if setting == pw_sed.CACHE_SED_AWAKE_DURATION: |
| 2331 | + return "20" |
| 2332 | + if setting == pw_sed.CACHE_SED_CLOCK_INTERVAL: |
| 2333 | + return "12600" |
| 2334 | + if setting == pw_sed.CACHE_SED_MAINTENANCE_INTERVAL: |
| 2335 | + return "60" |
| 2336 | + if setting == pw_sed.CACHE_SED_SLEEP_DURATION: |
| 2337 | + return "60" |
| 2338 | + if setting == pw_sense.CACHE_SENSE_HYSTERESIS_HUMIDITY_UPPER_BOUND: |
| 2339 | + return "60" |
| 2340 | + if setting == pw_sense.CACHE_SENSE_HYSTERESIS_HUMIDITY_LOWER_BOUND: |
| 2341 | + return "60" |
| 2342 | + if setting == pw_sense.CACHE_SENSE_HYSTERESIS_TEMPERATURE_UPPER_BOUND: |
| 2343 | + return "25.0" |
| 2344 | + if setting == pw_sense.CACHE_SENSE_HYSTERESIS_TEMPERATURE_LOWER_BOUND: |
| 2345 | + return "25.0" |
| 2346 | + return None |
| 2347 | + |
| 2348 | + def fake_cache_bool(dummy: object, setting: str) -> bool | None: |
| 2349 | + """Fake cache_bool retrieval.""" |
| 2350 | + if setting in ( |
| 2351 | + pw_sed.CACHE_SED_CLOCK_SYNC, |
| 2352 | + pw_sense.CACHE_SENSE_HYSTERESIS_TEMPERATURE_DIRECTION, |
| 2353 | + pw_sense.CACHE_SENSE_HYSTERESIS_HUMIDITY_DIRECTION, |
| 2354 | + ): |
| 2355 | + return True |
| 2356 | + if setting in ( |
| 2357 | + pw_sed.CACHE_SED_DIRTY, |
| 2358 | + pw_sense.CACHE_SENSE_HYSTERESIS_HUMIDITY_ENABLED, |
| 2359 | + pw_sense.CACHE_SENSE_HYSTERESIS_TEMPERATURE_ENABLED, |
| 2360 | + pw_sense.CACHE_SENSE_HYSTERESIS_CONFIG_DIRTY, |
| 2361 | + ): |
| 2362 | + return False |
| 2363 | + return None |
| 2364 | + |
| 2365 | + monkeypatch.setattr(pw_node.PlugwiseBaseNode, "_get_cache", fake_cache) |
| 2366 | + monkeypatch.setattr( |
| 2367 | + pw_node.PlugwiseBaseNode, "_get_cache_as_bool", fake_cache_bool |
| 2368 | + ) |
| 2369 | + mock_stick_controller = MockStickController() |
| 2370 | + sense_config_accepted = pw_responses.NodeAckResponse() |
| 2371 | + sense_config_accepted.deserialize( |
| 2372 | + construct_message(b"0100555555555555555500B5", b"0000") |
| 2373 | + ) |
| 2374 | + sense_config_failed = pw_responses.NodeAckResponse() |
| 2375 | + sense_config_failed.deserialize( |
| 2376 | + construct_message(b"0100555555555555555500B6", b"0000") |
| 2377 | + ) |
| 2378 | + |
| 2379 | + async def load_callback(event: pw_api.NodeEvent, mac: str) -> None: # type: ignore[name-defined] |
| 2380 | + """Load callback for event.""" |
| 2381 | + |
| 2382 | + test_sense = pw_sense.PlugwiseSense( |
| 2383 | + "1298347650AFBECD", |
| 2384 | + pw_api.NodeType.SENSE, |
| 2385 | + mock_stick_controller, |
| 2386 | + load_callback, |
| 2387 | + ) |
| 2388 | + assert not test_sense.cache_enabled |
| 2389 | + node_info = pw_api.NodeInfoMessage( |
| 2390 | + current_logaddress_pointer=None, |
| 2391 | + firmware=dt(2011, 11, 3, 13, 7, 33, tzinfo=UTC), |
| 2392 | + hardware="070030", |
| 2393 | + node_type=None, |
| 2394 | + relay_state=None, |
| 2395 | + ) |
| 2396 | + await test_sense.update_node_details(node_info) |
| 2397 | + await test_sense.load() |
| 2398 | + |
| 2399 | + # test hysteresis cache load |
| 2400 | + assert not test_sense.hysteresis_config_dirty |
| 2401 | + assert not test_sense.humidity_enabled |
| 2402 | + assert test_sense.humidity_upper_bound == 60 |
| 2403 | + assert test_sense.humidity_lower_bound == 60 |
| 2404 | + assert test_sense.humidity_direction |
| 2405 | + assert not test_sense.temperature_enabled |
| 2406 | + assert test_sense.temperature_upper_bound == 25 |
| 2407 | + assert test_sense.temperature_lower_bound == 25 |
| 2408 | + assert test_sense.temperature_direction |
| 2409 | + |
| 2410 | + # test humidity upper bound |
| 2411 | + with pytest.raises(ValueError): |
| 2412 | + await test_sense.set_hysteresis_humidity_upper_bound(0) |
| 2413 | + with pytest.raises(ValueError): |
| 2414 | + await test_sense.set_hysteresis_humidity_upper_bound(100) |
| 2415 | + assert not await test_sense.set_hysteresis_humidity_upper_bound(60) |
| 2416 | + assert not test_sense.hysteresis_config_dirty |
| 2417 | + with pytest.raises(ValueError): |
| 2418 | + await test_sense.set_hysteresis_humidity_upper_bound(55) |
| 2419 | + assert not test_sense.hysteresis_config_dirty |
| 2420 | + assert await test_sense.set_hysteresis_humidity_upper_bound(65) |
| 2421 | + assert test_sense.hysteresis_config_dirty |
| 2422 | + assert test_sense.humidity_upper_bound == 65 |
| 2423 | + assert not await test_sense.set_hysteresis_humidity_enabled(False) |
| 2424 | + assert await test_sense.set_hysteresis_humidity_enabled(True) |
| 2425 | + assert not await test_sense.set_hysteresis_humidity_direction(True) |
| 2426 | + assert await test_sense.set_hysteresis_humidity_direction(False) |
| 2427 | + |
| 2428 | + # Restore to original settings after failed config |
| 2429 | + awake_response1 = pw_responses.NodeAwakeResponse() |
| 2430 | + awake_response1.deserialize( |
| 2431 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2432 | + ) |
| 2433 | + mock_stick_controller.send_response = sense_config_failed |
| 2434 | + await test_sense._awake_response(awake_response1) # pylint: disable=protected-access |
| 2435 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2436 | + await test_sense._awake_response(awake_response1) # pylint: disable=protected-access |
| 2437 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2438 | + assert test_sense.hysteresis_config_dirty |
| 2439 | + |
| 2440 | + # Successful config |
| 2441 | + awake_response2 = pw_responses.NodeAwakeResponse() |
| 2442 | + awake_response2.deserialize( |
| 2443 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2444 | + ) |
| 2445 | + awake_response2.timestamp = awake_response1.timestamp + td( |
| 2446 | + seconds=pw_sed.AWAKE_RETRY |
| 2447 | + ) |
| 2448 | + mock_stick_controller.send_response = sense_config_accepted |
| 2449 | + await test_sense._awake_response(awake_response2) # pylint: disable=protected-access |
| 2450 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2451 | + await test_sense._awake_response(awake_response2) # pylint: disable=protected-access |
| 2452 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2453 | + assert not test_sense.hysteresis_config_dirty |
| 2454 | + assert test_sense.humidity_upper_bound == 65 |
| 2455 | + |
| 2456 | + # test humidity lower bound |
| 2457 | + with pytest.raises(ValueError): |
| 2458 | + await test_sense.set_hysteresis_humidity_lower_bound(0) |
| 2459 | + with pytest.raises(ValueError): |
| 2460 | + await test_sense.set_hysteresis_humidity_lower_bound(100) |
| 2461 | + assert not await test_sense.set_hysteresis_humidity_lower_bound(60) |
| 2462 | + assert not test_sense.hysteresis_config_dirty |
| 2463 | + with pytest.raises(ValueError): |
| 2464 | + await test_sense.set_hysteresis_humidity_lower_bound(70) |
| 2465 | + assert not test_sense.hysteresis_config_dirty |
| 2466 | + assert await test_sense.set_hysteresis_humidity_lower_bound(55) |
| 2467 | + assert test_sense.hysteresis_config_dirty |
| 2468 | + assert test_sense.humidity_lower_bound == 55 |
| 2469 | + |
| 2470 | + # Successful config |
| 2471 | + awake_response3 = pw_responses.NodeAwakeResponse() |
| 2472 | + awake_response3.deserialize( |
| 2473 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2474 | + ) |
| 2475 | + awake_response3.timestamp = awake_response2.timestamp + td( |
| 2476 | + seconds=pw_sed.AWAKE_RETRY |
| 2477 | + ) |
| 2478 | + mock_stick_controller.send_response = sense_config_accepted |
| 2479 | + await test_sense._awake_response(awake_response3) # pylint: disable=protected-access |
| 2480 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2481 | + await test_sense._awake_response(awake_response3) # pylint: disable=protected-access |
| 2482 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2483 | + assert not test_sense.hysteresis_config_dirty |
| 2484 | + assert test_sense.humidity_lower_bound == 55 |
| 2485 | + |
| 2486 | + # test temperature upper bound |
| 2487 | + with pytest.raises(ValueError): |
| 2488 | + await test_sense.set_hysteresis_temperature_upper_bound(0) |
| 2489 | + with pytest.raises(ValueError): |
| 2490 | + await test_sense.set_hysteresis_temperature_upper_bound(61) |
| 2491 | + assert not await test_sense.set_hysteresis_temperature_upper_bound(25) |
| 2492 | + assert not test_sense.hysteresis_config_dirty |
| 2493 | + with pytest.raises(ValueError): |
| 2494 | + await test_sense.set_hysteresis_temperature_upper_bound(24) |
| 2495 | + assert not test_sense.hysteresis_config_dirty |
| 2496 | + assert await test_sense.set_hysteresis_temperature_upper_bound(26) |
| 2497 | + assert test_sense.hysteresis_config_dirty |
| 2498 | + assert test_sense.temperature_upper_bound == 26 |
| 2499 | + assert not await test_sense.set_hysteresis_temperature_enabled(False) |
| 2500 | + assert await test_sense.set_hysteresis_temperature_enabled(True) |
| 2501 | + assert not await test_sense.set_hysteresis_temperature_direction(True) |
| 2502 | + assert await test_sense.set_hysteresis_temperature_direction(False) |
| 2503 | + |
| 2504 | + # Restore to original settings after failed config |
| 2505 | + awake_response4 = pw_responses.NodeAwakeResponse() |
| 2506 | + awake_response4.deserialize( |
| 2507 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2508 | + ) |
| 2509 | + awake_response4.timestamp = awake_response3.timestamp + td( |
| 2510 | + seconds=pw_sed.AWAKE_RETRY |
| 2511 | + ) |
| 2512 | + mock_stick_controller.send_response = sense_config_failed |
| 2513 | + await test_sense._awake_response(awake_response4) # pylint: disable=protected-access |
| 2514 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2515 | + await test_sense._awake_response(awake_response4) # pylint: disable=protected-access |
| 2516 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2517 | + assert test_sense.hysteresis_config_dirty |
| 2518 | + |
| 2519 | + # Successful config |
| 2520 | + awake_response5 = pw_responses.NodeAwakeResponse() |
| 2521 | + awake_response5.deserialize( |
| 2522 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2523 | + ) |
| 2524 | + awake_response5.timestamp = awake_response4.timestamp + td( |
| 2525 | + seconds=pw_sed.AWAKE_RETRY |
| 2526 | + ) |
| 2527 | + mock_stick_controller.send_response = sense_config_accepted |
| 2528 | + await test_sense._awake_response(awake_response5) # pylint: disable=protected-access |
| 2529 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2530 | + await test_sense._awake_response(awake_response5) # pylint: disable=protected-access |
| 2531 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2532 | + assert not test_sense.hysteresis_config_dirty |
| 2533 | + assert test_sense.temperature_upper_bound == 26 |
| 2534 | + |
| 2535 | + # test temperature lower bound |
| 2536 | + with pytest.raises(ValueError): |
| 2537 | + await test_sense.set_hysteresis_temperature_lower_bound(0) |
| 2538 | + with pytest.raises(ValueError): |
| 2539 | + await test_sense.set_hysteresis_temperature_lower_bound(61) |
| 2540 | + assert not await test_sense.set_hysteresis_temperature_lower_bound(25) |
| 2541 | + assert not test_sense.hysteresis_config_dirty |
| 2542 | + with pytest.raises(ValueError): |
| 2543 | + await test_sense.set_hysteresis_temperature_lower_bound(27) |
| 2544 | + assert not test_sense.hysteresis_config_dirty |
| 2545 | + assert await test_sense.set_hysteresis_temperature_lower_bound(24) |
| 2546 | + assert test_sense.hysteresis_config_dirty |
| 2547 | + assert test_sense.temperature_lower_bound == 24 |
| 2548 | + |
| 2549 | + # Successful config |
| 2550 | + awake_response6 = pw_responses.NodeAwakeResponse() |
| 2551 | + awake_response6.deserialize( |
| 2552 | + construct_message(b"004F555555555555555500", b"FFFE") |
| 2553 | + ) |
| 2554 | + awake_response6.timestamp = awake_response5.timestamp + td( |
| 2555 | + seconds=pw_sed.AWAKE_RETRY |
| 2556 | + ) |
| 2557 | + mock_stick_controller.send_response = sense_config_accepted |
| 2558 | + await test_sense._awake_response(awake_response6) # pylint: disable=protected-access |
| 2559 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2560 | + await test_sense._awake_response(awake_response6) # pylint: disable=protected-access |
| 2561 | + await asyncio.sleep(0.001) # Ensure time for task to be executed |
| 2562 | + assert not test_sense.hysteresis_config_dirty |
| 2563 | + assert test_sense.temperature_lower_bound == 24 |
| 2564 | + |
2315 | 2565 | @pytest.mark.asyncio |
2316 | 2566 | async def test_switch_node(self, monkeypatch: pytest.MonkeyPatch) -> None: |
2317 | 2567 | """Testing properties of switch.""" |
|
0 commit comments