diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index e5da5fe..9520013 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -57,3 +57,28 @@ jobs: pip install -r requirements.txt -r test-requirements.txt --upgrade --upgrade-strategy eager - name: Run pytest on ${{ matrix.python-version }} run: pytest + + coverage: + name: "Check code coverage" + runs-on: ubuntu-latest + needs: lint + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - uses: actions/cache@v2 + with: + path: ${{ env.pythonLocation }} + key: ${{ env.pythonLocation }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('test-requirements.txt') }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt -r test-requirements.txt --upgrade --upgrade-strategy eager + - name: Run pytest on ${{ matrix.python-version }} + run: pytest --cov=./ --cov-report=xml + - name: "Upload coverage to Codecov" + uses: codecov/codecov-action@v2 + with: + fail_ci_if_error: true diff --git a/README.md b/README.md index f33dc14..641bf46 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # pysmartthings +[![CI Status](https://github.com/andrewsayre/pysmartthings/workflows/CI/badge.svg)](https://github.com/andrewsayre/pysmartthings/actions) +[![codecov](https://codecov.io/gh/andrewsayre/pysmartthings/branch/dev/graph/badge.svg?token=Q13LDPU5MF)](https://codecov.io/gh/andrewsayre/pysmartthings) [![image](https://img.shields.io/pypi/v/pysmartthings.svg)](https://pypi.org/project/pysmartthings/) [![image](https://img.shields.io/pypi/pyversions/pysmartthings.svg)](https://pypi.org/project/pysmartthings/) [![image](https://img.shields.io/pypi/l/pysmartthings.svg)](https://pypi.org/project/pysmartthings/) diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..7a9eea7 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +codecov: + branch: dev +coverage: + status: + project: + default: + target: 90 + threshold: 0.09 +comment: false diff --git a/pysmartthings/capability.py b/pysmartthings/capability.py index dd175f0..5e87f8b 100644 --- a/pysmartthings/capability.py +++ b/pysmartthings/capability.py @@ -44,6 +44,7 @@ "energyMeter": ["energy"], "equivalentCarbonDioxideMeasurement": ["equivalentCarbonDioxideMeasurement"], "execute": ["data"], + "fanOscillationMode": ["fanOscillationMode", "supportedFanOscillationModes"], "fanSpeed": ["fanSpeed"], "filterStatus": ["filterStatus"], "formaldehydeMeasurement": ["formaldehydeLevel"], @@ -187,6 +188,7 @@ class Capability: energy_meter = "energyMeter" equivalent_carbon_dioxide_measurement = "equivalentCarbonDioxideMeasurement" execute = "execute" + fan_oscillation_mode = "fanOscillationMode" fan_speed = "fanSpeed" filter_status = "filterStatus" formaldehyde_measurement = "formaldehydeMeasurement" @@ -275,6 +277,7 @@ class Attribute: energy = "energy" equivalent_carbon_dioxide_measurement = "equivalentCarbonDioxideMeasurement" fan_mode = "fanMode" + fan_oscillation_mode = "fanOscillationMode" fan_speed = "fanSpeed" filter_status = "filterStatus" fine_dust_level = "fineDustLevel" diff --git a/pysmartthings/device.py b/pysmartthings/device.py index 81195c9..a5d92f1 100644 --- a/pysmartthings/device.py +++ b/pysmartthings/device.py @@ -58,6 +58,7 @@ class Command: set_color = "setColor" set_color_temperature = "setColorTemperature" set_cooling_setpoint = "setCoolingSetpoint" + set_fan_oscillation_mode = "setFanOscillationMode" set_fan_mode = "setFanMode" set_fan_speed = "setFanSpeed" set_heating_setpoint = "setHeatingSetpoint" @@ -611,6 +612,11 @@ def fan_mode(self) -> Optional[str]: """Get the fan mode attribute.""" return self._attributes[Attribute.fan_mode].value + @property + def fan_oscillation_mode(self) -> Optional[str]: + """Get the fan oscillation mode attribute.""" + return self._attributes[Attribute.fan_oscillation_mode].value + @property def supported_ac_fan_modes(self) -> Sequence[str]: """Get the supported AC fan modes attribute.""" @@ -1180,6 +1186,20 @@ async def set_fan_mode( self.status.update_attribute_value(Attribute.fan_mode, mode) return result + async def set_fan_oscillation_mode( + self, mode: str, *, set_status: bool = False, component_id: str = "main" + ): + """Call the setFanOscillationMode command.""" + result = await self.command( + component_id, + Capability.fan_oscillation_mode, + Command.set_fan_oscillation_mode, + [mode], + ) + if result and set_status: + self.status.update_attribute_value(Attribute.fan_oscillation_mode, mode) + return result + async def set_air_flow_direction( self, direction: str, *, set_status: bool = False, component_id: str = "main" ): diff --git a/tests/json/device_command_post_set_fan_oscillation_mode.json b/tests/json/device_command_post_set_fan_oscillation_mode.json new file mode 100644 index 0000000..41f2da6 --- /dev/null +++ b/tests/json/device_command_post_set_fan_oscillation_mode.json @@ -0,0 +1,12 @@ +{ + "commands": [ + { + "component": "main", + "capability": "fanOscillationMode", + "command": "setFanOscillationMode", + "arguments": [ + "all" + ] + } + ] +} \ No newline at end of file diff --git a/tests/test_device.py b/tests/test_device.py index cce58db..a1f9e19 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -794,6 +794,18 @@ async def test_set_fan_mode(api): assert await device.set_fan_mode("auto", set_status=True) assert device.status.fan_mode == "auto" + @staticmethod + @pytest.mark.asyncio + async def test_set_fan_oscillation_mode(api): + """Tests the set_fan_oscillation_mode method.""" + # Arrange + device = DeviceEntity(api, device_id=DEVICE_ID) + # Act/Assert + assert await device.set_fan_oscillation_mode("all") + assert device.status.fan_oscillation_mode is None + assert await device.set_fan_oscillation_mode("all", set_status=True) + assert device.status.fan_oscillation_mode == "all" + @staticmethod @pytest.mark.asyncio async def test_set_air_flow_direction(api): @@ -1495,6 +1507,7 @@ def test_well_known_attributes(): status.update_attribute_value(Attribute.three_axis, [0, 0, 0]) status.update_attribute_value(Attribute.supported_ac_modes, ["auto", "cool"]) status.update_attribute_value(Attribute.fan_mode, "low") + status.update_attribute_value(Attribute.fan_oscillation_mode, "fixed") status.update_attribute_value(Attribute.supported_ac_fan_modes, ["auto", "low"]) # Act/Assert assert status.humidity == 50