From 958691d58f486c6d178d321fed3bbfa5eddc3ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20S=C3=A1nchez-Gallego?= Date: Fri, 24 Nov 2023 12:55:45 -0800 Subject: [PATCH] status now accepts outlet argument --- CHANGELOG.md | 4 +++ src/lvmnps/actor/commands/status.py | 21 +++++++++-- src/lvmnps/actor/schema.json | 55 ++++++++++++++++------------- tests/config.yaml | 4 +-- tests/conftest.py | 2 ++ tests/test_actor.py | 33 +++++++++++++++-- 6 files changed, 89 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2cac5e..f978d28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 1.0.2 +### ✨ Improved + +* It's now possible to call `status` with an outlet and a summary of the status for only that outlet will be issued. + ### 🔧 Fixed * Issue an NPS refresh before issuing any status. diff --git a/src/lvmnps/actor/commands/status.py b/src/lvmnps/actor/commands/status.py index 662b758..f92a962 100644 --- a/src/lvmnps/actor/commands/status.py +++ b/src/lvmnps/actor/commands/status.py @@ -10,6 +10,10 @@ from typing import TYPE_CHECKING +import click + +from lvmnps.actor.commands.onoff import OutletNameParamType + from . import lvmnps_command_parser @@ -21,12 +25,25 @@ @lvmnps_command_parser.command() -async def status(command: NPSCommand): - """Outputs the status of the network power switch.""" +@click.argument("OUTLET", type=OutletNameParamType(), required=False) +async def status(command: NPSCommand, outlet: int | str | None = None): + """Outputs the status of the network power switch. + + If an OUTLET is passed, returns only the status of that outlet. + + """ nps = command.actor.nps await nps.refresh() + + if outlet is not None: + try: + outlet_obj = nps.get(outlet) + return command.finish(outlet_info=outlet_obj.model_dump()) + except Exception: + return command.fail(f"Invalid outlet {outlet!r}.") + command.info(nps_type=nps.nps_type) command.info(outlet_names=list(nps.outlets)) diff --git a/src/lvmnps/actor/schema.json b/src/lvmnps/actor/schema.json index c331ee1..17e33b0 100644 --- a/src/lvmnps/actor/schema.json +++ b/src/lvmnps/actor/schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema", - "definitions": { + "$defs": { "script": { "type": "object", "description": "Information about a user script", @@ -12,6 +12,30 @@ }, "required": ["name", "args", "running", "thread_id"], "additionalProperties": false + }, + "outlet_field": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Original name of the outlet" + }, + "normalised_name": { + "type": "string", + "description": "Normalised outlet name" + }, + "id": { + "type": "integer", + "description": "Numerical outlet identifier" + }, + "state": { + "type": "boolean", + "description": "Outlet state (true=ON)" + } + }, + "required": ["name", "normalised_name", "id", "state"], + "additionalProperties": true, + "description": "Properties of each outlet" } }, "type": "object", @@ -25,31 +49,14 @@ "outlets": { "type": "array", "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Original name of the outlet" - }, - "normalised_name": { - "type": "string", - "description": "Normalised outlet name" - }, - "id": { - "type": "integer", - "description": "Numerical outlet identifier" - }, - "state": { - "type": "boolean", - "description": "Outlet state (true=ON)" - } - }, - "required": ["name", "normalised_name", "id", "state"], - "additionalProperties": true, - "description": "Properties of each outlet" + "$ref": "#/$defs/outlet_field" } }, - "script": { "$ref": "#/definitions/script" }, + "outlet_info": { + "$ref": "#/$defs/outlet_field", + "description": "Information about a specific outlet" + }, + "script": { "$ref": "#/$defs/script" }, "scripts": { "type": "array", "items": { "$ref": "#/definitions/script" } } }, "additionalProperties": false diff --git a/tests/config.yaml b/tests/config.yaml index 3883331..5cff1f2 100644 --- a/tests/config.yaml +++ b/tests/config.yaml @@ -4,8 +4,8 @@ nps: init_parameters: host: 127.0.0.1 port: 8088 - user: admin - password: admin + user: lvm + password: lvm00 actor: name: lvmnps.test diff --git a/tests/conftest.py b/tests/conftest.py index b24f1cf..118b3fa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -181,6 +181,8 @@ async def nps_actor(mocker: MockerFixture, lvmnps_config: Configuration): actor = NPSActor.from_config(lvmnps_config) actor.nps = mocker.MagicMock(spec=DLIClient) + actor.nps.get = mocker.MagicMock(side_effect=lambda x: actor.nps.outlets[x]) + actor.nps.nps_type = "dli" actor.nps.outlets = {"outlet_1": DLIOutletModel(id=1, name="outlet_1")} diff --git a/tests/test_actor.py b/tests/test_actor.py index 44f04cd..4c98061 100644 --- a/tests/test_actor.py +++ b/tests/test_actor.py @@ -49,7 +49,7 @@ async def test_actor_netio_nps(lvmnps_config: Configuration): assert isinstance(actor.nps, NetIOClient) -async def test_command_status(nps_actor: NPSActor, mocker: MockerFixture): +async def test_command_status(nps_actor: NPSActor): cmd = await nps_actor.invoke_mock_command("status") await cmd @@ -73,7 +73,36 @@ async def test_command_status(nps_actor: NPSActor, mocker: MockerFixture): } -async def test_command_refresh(nps_actor: NPSActor, mocker: MockerFixture): +async def test_command_status_outlet(nps_actor: NPSActor): + cmd = await nps_actor.invoke_mock_command("status outlet_1") + await cmd + + assert cmd.status.did_succeed + assert len(cmd.replies) == 2 + assert cmd.replies[-1].body == { + "outlet_info": { + "critical": False, + "cycle_delay": None, + "id": 1, + "index": 0, + "locked": False, + "name": "outlet_1", + "normalised_name": "outlet_1", + "physical_state": False, + "state": False, + "transient_state": False, + } + } + + +async def test_command_status_invalid_outlet(nps_actor: NPSActor): + cmd = await nps_actor.invoke_mock_command("status outlet_5") + await cmd + + assert cmd.status.did_fail + + +async def test_command_refresh(nps_actor: NPSActor): cmd = await nps_actor.invoke_mock_command("refresh") await cmd