Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fan): added support for gorilla fan v2 #1988

Merged
merged 6 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions custom_components/tuya_local/devices/gorilla_fan_v2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Gorilla Fan V2
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preferably there should be a products section here, with product id and name (with branding), and the top level name should be generic (Ceiling fan, for example)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see from #1971 that the brand is Atomberg, and the product name is Gorilla. The file naming and the name in the products section should reflect this. I'm not sure whether the V2 is part of the product name, or it is a misunderstanding that whatever config you originally installed this as is "V1" (but there is no atomberg or gorilla config, so there is no need to use v2 here as disambiguation if it is not part of the actual product name).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fan in question is from Atomberg and is referred to as the Renesa fan. However, this name is not present in any of our systems. In both the Tuya Dashboard and the Smart Life App, it is listed as the Gorilla Fan V2. Therefore, I used this name for consistency.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the product id? If you cannot get it from the cloud, it is also possible to read from python -m tinytuya scan

primary_entity:
entity: fan
dps:
- id: 1
type: boolean
name: switch
icon: mdi:ceiling-fan
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
- id: 2
type: string
name: preset_mode
mapping:
- dps_value: normal
value: Normal
- dps_value: boost
value: Boost
- id: 3
type: integer
name: speed
range:
min: 1
max: 5

secondary_entities:
- entity: light
name: Night Light
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
icon: mdi:ceiling-fan-light
dps:
- id: 15
type: boolean
name: switch
- entity: switch
name: Boost
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
icon: mdi:car-turbocharger
dps:
- id: 115
type: boolean
name: switch
- entity: switch
name: Sleep
icon: mdi:sleep
dps:
- id: 113
type: boolean
name: switch
- entity: number
name: Timer
icon: mdi:timer-edit
translation_key: timer
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
category: config
dps:
- id: 102
type: integer
name: value
unit: min
range:
min: 0
max: 360
71 changes: 71 additions & 0 deletions tests/devices/test_gorilla_fan_v2.py
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from homeassistant.components.fan import FanEntityFeature

from ..const import FAN_PAYLOAD
from ..helpers import assert_device_properties_set
from .base_device_tests import TuyaDeviceTestCase

SWITCH_DPS = "1"
PRESET_DPS = "2"
FANMODE_DPS = "3"

class TestGorillaFanV2(TuyaDeviceTestCase):
__test__ = True

def setUp(self):
self.setUpForConfig("gorilla_fan_v2.yaml", FAN_PAYLOAD)
self.subject = self.entities.get("fan")

def test_supported_features(self):
self.assertEqual(
self.subject.supported_features,
(
FanEntityFeature.PRESET_MODE
| FanEntityFeature.SET_SPEED
),
)

def test_preset_mode(self):
self.dps[PRESET_DPS] = "normal"
self.assertEqual(self.subject.preset_mode, "Normal")

self.dps[PRESET_DPS] = "boost"
self.assertEqual(self.subject.preset_mode, "Boost")

def test_preset_modes(self):
self.assertCountEqual(self.subject.preset_modes, ["normal", "boost"])

async def test_set_preset_mode_to_normal(self):
async with assert_device_properties_set(

Check failure on line 38 in tests/devices/test_gorilla_fan_v2.py

View workflow job for this annotation

GitHub Actions / tests (3.12)

TestGorillaFanV2.test_set_preset_mode_to_normal AssertionError: Expected {'2': 'normal'}, got {'2': 'Normal'}
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
self.subject._device,
{PRESET_DPS: "normal"},
):
await self.subject.async_set_preset_mode("Normal")

async def test_set_preset_mode_to_boost(self):
async with assert_device_properties_set(

Check failure on line 45 in tests/devices/test_gorilla_fan_v2.py

View workflow job for this annotation

GitHub Actions / tests (3.12)

TestGorillaFanV2.test_set_preset_mode_to_boost AssertionError: Expected {'2': 'boost'}, got {'2': 'Boost'}
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
self.subject._device,
{PRESET_DPS: "boost"},
):
await self.subject.async_set_preset_mode("Boost")

def test_speed(self):
self.dps[PRESET_DPS] = "normal"
self.dps[FANMODE_DPS] = 3
self.assertEqual(self.subject.percentage, 60)

async def test_set_speed_in_normal_mode(self):
self.dps[PRESET_DPS] = "normal"
async with assert_device_properties_set(self.subject._device, {FANMODE_DPS: 3}):

Check failure on line 58 in tests/devices/test_gorilla_fan_v2.py

View workflow job for this annotation

GitHub Actions / tests (3.12)

TestGorillaFanV2.test_set_speed_in_normal_mode AssertionError: Expected {'3': 3}, got {'3': 3, '1': True}
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
await self.subject.async_set_percentage(60)

async def test_set_speed_in_boost_mode(self):
self.dps[PRESET_DPS] = "boost"
async with assert_device_properties_set(self.subject._device, {FANMODE_DPS: 5}):

Check failure on line 63 in tests/devices/test_gorilla_fan_v2.py

View workflow job for this annotation

GitHub Actions / tests (3.12)

TestGorillaFanV2.test_set_speed_in_boost_mode AssertionError: Expected {'3': 5}, got {'3': 5, '1': True}
aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
await self.subject.async_set_percentage(100)

async def test_set_speed_to_zero_turns_off(self):
async with assert_device_properties_set(
self.subject._device, {SWITCH_DPS: False}
):
await self.subject.async_set_percentage(0)

22 changes: 22 additions & 0 deletions tests/test_fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,25 @@ async def test_init_entry_fails_if_config_is_missing(hass):
except ValueError:
pass
m_add_entities.assert_not_called()

aravindjaimon marked this conversation as resolved.
Show resolved Hide resolved
@pytest.mark.asyncio
async def test_init_entry_for_gorilla_fan_v2(hass):
"""Test initialisation for Gorilla Fan V2"""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_TYPE: "gorilla_fan_v2",
CONF_DEVICE_ID: "dummy",
CONF_PROTOCOL_VERSION: "auto",
},
)
m_add_entities = Mock()
m_device = AsyncMock()

hass.data[DOMAIN] = {}
hass.data[DOMAIN]["dummy"] = {}
hass.data[DOMAIN]["dummy"]["device"] = m_device

await async_setup_entry(hass, entry, m_add_entities)
assert type(hass.data[DOMAIN]["dummy"]["fan"]) == TuyaLocalFan
m_add_entities.assert_called_once()
Loading