[Local Agent] Synced go/nest-cl/280677, go/nest-cl/280072 and fixed b/220206029 Change-Id: Icfec6483a14098f3931bb7d3386250c8729dd1a5
diff --git a/README.md b/README.md index e91eda6..9f1a936 100644 --- a/README.md +++ b/README.md
@@ -69,9 +69,10 @@ > For development, we might want to use `pip install -e .`. > (See documentation of the "-e" option [here](https://pip.pypa.io/en/stable/cli/pip_install/#cmdoption-e)) -5. Copy the config file [example_config.ini](https://testsuite-smarthome-matter.googlesource.com/local-agent/+/refs/heads/master/example_config.ini) to your host at ```~/.config/google/local_agent_config.ini```. - -6. Fill the names of your Matter controller extension packages (The extension you built in the above ```Matter extension packages``` section) in ```~/.config/google/local_agent_config.ini```. See [example_config.ini](https://testsuite-smarthome-matter.googlesource.com/local-agent/+/refs/heads/master/example_config.ini#20) for references. +5. (Optional) Only needed if you want to register your own Matter extension packages: + - Create an empty config file named ```local_agent_config.ini``` under ```~/.config/google/```. + - Copy the content in [example_config.ini](https://testsuite-smarthome-matter.googlesource.com/local-agent/+/refs/heads/master/example_config.ini) to the ```local_agent_config.ini```. + - Fill the names of your Matter controller extension packages (The extension you built in the above ```Matter extension packages``` section) in ```local_agent_config.ini```. See [example_config.ini](https://testsuite-smarthome-matter.googlesource.com/local-agent/+/refs/heads/master/example_config.ini#20) for references. ## Usage @@ -178,4 +179,28 @@ 20211207 14:41:20.456 I local_agent.py:249 Stopped reporting info because stop event is set. ``` + +## Troubleshooting + +### I can't install the Local Agent on my host. + +Make sure you're using Ubuntu LTS on your host and python version is ```>= 3.7```. + +### My Matter device is not detected by Local Agent. + +Local Agent uses Pigweed RPC debug interface for communicating with Matter devices, make sure +your Matter device implements the Pigweed RPC endpoint and your Matter device class is already +registered in GDM (step 5 in the ```Setup``` section). You can also try detecting the devices +via GDM directly by following the [GDM instructions](https://github.com/google/gazoo-device#detecting-devices). + +### What if my Matter device is accidentally shutdown during the test execution? + +If the device gets shutdown or rebooted during test execution, GDM may crash and you'll need to close +the Local Agent and restart it again after the test execution. Local agent does not need to be re-linked.. + +### What if my host machine is accidentally shutdown during the test execution? + +Local agent will be closed forcely and you'll need to restart it. Local agent does not need to be re-linked. + + Please contact your **Google Developer Support** or **smarthome-testsuite-contrib@google.com** if you encounter any unexpected problems. \ No newline at end of file
diff --git a/local_agent/tests/unit_tests/test_command_handlers/test_cluster_handlers/test_color.py b/local_agent/tests/unit_tests/test_command_handlers/test_cluster_handlers/test_color.py new file mode 100644 index 0000000..7b44894 --- /dev/null +++ b/local_agent/tests/unit_tests/test_command_handlers/test_cluster_handlers/test_color.py
@@ -0,0 +1,82 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for Color Control cluster handler.""" +from typing import Any +import unittest +from unittest import mock + +from gazoo_device import errors + +from local_agent.translation_layer.command_handlers import base +from local_agent.translation_layer.command_handlers.cluster_handlers import color + +_FAKE_ERROR_MSG = 'fake-error-msg' +_FAKE_HUE = 10 +_FAKE_SATURATION = 11 + + +class DummyHandler(base.BaseCommandHandler, color.ColorControlHandler): + """Dummy handler for testing Color control cluster handler.""" + + def __init__(self, dut: Any) -> None: + super().__init__(dut) + self.endpoint = self.dut.color_temperature_light + + +class ColorControlHandlerTest(unittest.TestCase): + """Unit tests for Color Control cluster handler.""" + + def setUp(self): + super().setUp() + self.mock_dut = mock.Mock() + self.handler = DummyHandler(self.mock_dut) + + def test_get_color_on_success(self): + """Verifies the _get_color method on success.""" + self.handler.endpoint.color.current_hue = _FAKE_HUE + self.handler.endpoint.color.current_saturation = _FAKE_SATURATION + expected_result = {'hue': _FAKE_HUE, 'saturation': _FAKE_SATURATION} + + self.assertEqual(expected_result, self.handler._get_color({})) + + def test_get_color_on_failure(self): + """Verifies the _get_color method on failure.""" + mock_current_hue= mock.PropertyMock( + side_effect=errors.DeviceError(_FAKE_ERROR_MSG)) + type(self.handler.endpoint.color).current_hue = mock_current_hue + with self.assertRaisesRegex(errors.DeviceError, _FAKE_ERROR_MSG): + self.handler._get_color({}) + + def test_set_color_on_success(self): + """Verifies the _set_color method on success.""" + self.handler._set_color( + {'hue': _FAKE_HUE, 'saturation': _FAKE_SATURATION}) + + self.handler.endpoint.color.move_to_hue.assert_called_once_with( + hue=_FAKE_HUE) + self.handler.endpoint.color.move_to_saturation.assert_called_once_with( + saturation=_FAKE_SATURATION) + + def test_set_color_on_failure(self): + """Verifies the _set_color method on failure.""" + self.handler.endpoint.color.move_to_hue.side_effect = ( + errors.DeviceError(_FAKE_ERROR_MSG)) + with self.assertRaisesRegex(errors.DeviceError, _FAKE_ERROR_MSG): + self.handler._set_color( + {'hue': _FAKE_HUE, 'saturation': _FAKE_SATURATION}) + + +if __name__ == '__main__': + unittest.main(failfast=True)
diff --git a/local_agent/tests/unit_tests/test_command_handlers/test_color_temperature_light.py b/local_agent/tests/unit_tests/test_command_handlers/test_color_temperature_light.py new file mode 100644 index 0000000..bd060f8 --- /dev/null +++ b/local_agent/tests/unit_tests/test_command_handlers/test_color_temperature_light.py
@@ -0,0 +1,39 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for Color Temperature Light endpoint command handler.""" +import unittest +from unittest import mock + +from local_agent.translation_layer.command_handlers import color_temperature_light + + +class ColorTemperatureLightCommandHandlerTest(unittest.TestCase): + """Unit tests for ColorTemperatureLightCommandHandler.""" + + def setUp(self): + super().setUp() + self.mock_dut = mock.Mock() + self.handler = color_temperature_light.ColorTemperatureLightCommandHandler( + self.mock_dut) + + def test_endpoint_initialization(self): + """Verifies if endpoint instance is initialized successfully.""" + self.assertEqual( + self.mock_dut.color_temperature_light, self.handler.endpoint) + + + +if __name__ == '__main__': + unittest.main(failfast=True)
diff --git a/local_agent/translation_layer/command_handlers/cluster_handlers/color.py b/local_agent/translation_layer/command_handlers/cluster_handlers/color.py new file mode 100644 index 0000000..c329489 --- /dev/null +++ b/local_agent/translation_layer/command_handlers/cluster_handlers/color.py
@@ -0,0 +1,72 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Module for Color cluster command handling.""" +from typing import Any, Dict +from gazoo_device import errors +from local_agent import logger as logger_module + +logger = logger_module.get_logger() + + +class ColorControlHandler: + """Mixin for Matter Color Control cluster handler. + + The mixin assumes self.dut and self.endpoint are set. + """ + + _GET_COLOR = 'getColor' + _SET_COLOR = 'setColor' + + SUPPORTED_METHODS = {_GET_COLOR, _SET_COLOR} + + def _get_color(self, params: Dict[str, Any]) -> Dict[str, int]: + """Queries the color attributes of the device. + + Color attributes include CurrentHue and CurrentSaturation. + + Returns: + A dict containing hue and saturation value. + + Raises: + DeviceError: getting color attribute fails. + """ + del params # not used + try: + color_configurations = {} + color_configurations['hue'] = self.endpoint.color.current_hue + color_configurations['saturation'] = ( + self.endpoint.color.current_saturation) + return color_configurations + except errors.DeviceError as exc: + logger.exception(f'Getting color of {self.dut.name} failed.') + raise exc + + def _set_color(self, params: Dict[str, Any]) -> None: + """Sets color of the device. + + Raises: + DeviceError: turning light on fails. + """ + self.validate_key_in_params( + params=params, param_key='hue', expected_type=int) + self.validate_key_in_params( + params=params, param_key='saturation', expected_type=int) + try: + self.endpoint.color.move_to_hue(hue=params['hue']) + self.endpoint.color.move_to_saturation( + saturation=params['saturation']) + except errors.DeviceError as exc: + logger.exception(f'Setting color of {self.dut.name} on failed.') + raise exc
diff --git a/local_agent/translation_layer/command_handlers/color_temperature_light.py b/local_agent/translation_layer/command_handlers/color_temperature_light.py new file mode 100644 index 0000000..7c07c6e --- /dev/null +++ b/local_agent/translation_layer/command_handlers/color_temperature_light.py
@@ -0,0 +1,39 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Module for Color Temperature Light endpoint command handler.""" +from typing import Any, Dict + +from gazoo_device import errors + +from local_agent import logger as logger_module +from local_agent.translation_layer.command_handlers import base +from local_agent.translation_layer.command_handlers.cluster_handlers import color + + +logger = logger_module.get_logger() + +ENDPOINT_CAPABILITY = 'color_temperature_light' + + +# TODO(b/209362086): Add other clusters to this handler. +class ColorTemperatureLightCommandHandler( + base.BaseCommandHandler, color.ColorControlHandler): + """Command handler for OnOffLight endpoint.""" + + SUPPORTED_METHODS = (color.ColorControlHandler.SUPPORTED_METHODS) + + def __init__(self, dut: Any) -> None: + super().__init__(dut) + self.endpoint = self.dut.color_temperature_light
diff --git a/local_agent/translation_layer/command_handlers/handler_registry.py b/local_agent/translation_layer/command_handlers/handler_registry.py index e68da93..fd9b4f8 100644 --- a/local_agent/translation_layer/command_handlers/handler_registry.py +++ b/local_agent/translation_layer/command_handlers/handler_registry.py
@@ -19,6 +19,7 @@ """ import immutabledict +from local_agent.translation_layer.command_handlers import color_temperature_light from local_agent.translation_layer.command_handlers import common from local_agent.translation_layer.command_handlers import on_off_light from local_agent.translation_layer.command_handlers import door_lock @@ -27,8 +28,9 @@ # GDM capability -> Matter endpoint command handlers # TODO(b/216406955): Make command handler mapping dynamic generated GDM_CAPABILITIES_TO_COMMAND_HANDLERS = immutabledict.immutabledict({ + color_temperature_light.ENDPOINT_CAPABILITY: + color_temperature_light.ColorTemperatureLightCommandHandler, common.PWRPC_COMMON_CAPABILITY: common.CommonCommandHandler, on_off_light.ENDPOINT_CAPABILITY: on_off_light.OnOffLightCommandHandler, door_lock.ENDPOINT_CAPABILITY: door_lock.DoorLockCommandHandler - # TODO(b/216407280) Add color temperature command handler. })