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

Add support for VeSync Fans #36132

Merged
merged 16 commits into from
Sep 4, 2020
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
24 changes: 23 additions & 1 deletion homeassistant/components/vesync/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
VS_DISPATCHERS,
VS_MANAGER,
VS_SWITCHES,
VS_FANS,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -80,20 +81,27 @@ async def async_setup_entry(hass, config_entry):
hass.data[DOMAIN][VS_MANAGER] = manager

switches = hass.data[DOMAIN][VS_SWITCHES] = []
fans = hass.data[DOMAIN][VS_FANS] = []

hass.data[DOMAIN][VS_DISPATCHERS] = []

bdraco marked this conversation as resolved.
Show resolved Hide resolved
if device_dict[VS_SWITCHES]:
switches.extend(device_dict[VS_SWITCHES])
hass.async_create_task(forward_setup(config_entry, "switch"))

if device_dict[VS_FANS]:
fans.extend(device_dict[VS_FANS])
hass.async_create_task(forward_setup(config_entry, "fan"))

async def async_new_device_discovery(service):
"""Discover if new devices should be added."""
manager = hass.data[DOMAIN][VS_MANAGER]
switches = hass.data[DOMAIN][VS_SWITCHES]
fans = hass.data[DOMAIN][VS_FANS]

dev_dict = await async_process_devices(hass, manager)
switch_devs = dev_dict.get(VS_SWITCHES, [])
fan_devs = dev_dict.get(VS_FANS, [])

switch_set = set(switch_devs)
new_switches = list(switch_set.difference(switches))
Expand All @@ -105,6 +113,16 @@ async def async_new_device_discovery(service):
switches.extend(new_switches)
hass.async_create_task(forward_setup(config_entry, "switch"))

fan_set = set(fan_devs)
new_fans = list(fan_set.difference(fans))
if new_fans and fans:
fans.extend(new_fans)
async_dispatcher_send(hass, VS_DISCOVERY.format(VS_FANS), new_fans)
return
if new_fans and not fans:
fans.extend(new_fans)
hass.async_create_task(forward_setup(config_entry, "fan"))

hass.services.async_register(
DOMAIN, SERVICE_UPDATE_DEVS, async_new_device_discovery
)
Expand All @@ -119,7 +137,11 @@ async def async_unload_entry(hass, entry):
if hass.data[DOMAIN][VS_SWITCHES]:
remove_switches = await forward_unload(entry, "switch")

TheGardenMonkey marked this conversation as resolved.
Show resolved Hide resolved
if remove_switches:
remove_fans = False
if hass.data[DOMAIN][VS_FANS]:
remove_fans = await forward_unload(entry, "fan")

if remove_switches and remove_fans:
hass.services.async_remove(DOMAIN, SERVICE_UPDATE_DEVS)
del hass.data[DOMAIN]
return True
Expand Down
9 changes: 7 additions & 2 deletions homeassistant/components/vesync/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from homeassistant.helpers.entity import ToggleEntity

from .const import VS_SWITCHES
from .const import VS_SWITCHES, VS_FANS

_LOGGER = logging.getLogger(__name__)

Expand All @@ -12,9 +12,14 @@ async def async_process_devices(hass, manager):
"""Assign devices to proper component."""
devices = {}
devices[VS_SWITCHES] = []
devices[VS_FANS] = []

await hass.async_add_executor_job(manager.update)

if manager.fans:
devices[VS_FANS].extend(manager.fans)
_LOGGER.info("%d VeSync fans found", len(manager.fans))

if manager.outlets:
devices[VS_SWITCHES].extend(manager.outlets)
_LOGGER.info("%d VeSync outlets found", len(manager.outlets))
Expand Down Expand Up @@ -49,7 +54,7 @@ def name(self):

@property
def is_on(self):
"""Return True if switch is on."""
"""Return True if device is on."""
return self.device.device_status == "on"

@property
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/vesync/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
SERVICE_UPDATE_DEVS = "update_devices"

VS_SWITCHES = "switches"
VS_FANS = "fans"
VS_MANAGER = "manager"
120 changes: 120 additions & 0 deletions homeassistant/components/vesync/fan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""Support for Etekcity VeSync fans."""
import logging

from homeassistant.components.fan import FanEntity, SUPPORT_SET_SPEED
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect

from .common import VeSyncDevice
from .const import DOMAIN, VS_DISCOVERY, VS_DISPATCHERS, VS_FANS

_LOGGER = logging.getLogger(__name__)

DEV_TYPE_TO_HA = {
"LV-PUR131S": "fan",
}

FAN_SPEEDS = ["auto", "low", "medium", "high"]


async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the VeSync fan platform."""

async def async_discover(devices):
Copy link
Member

@MartinHjelmare MartinHjelmare Sep 4, 2020

Choose a reason for hiding this comment

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

async_discover could be a callback since we don't await inside.

"""Add new devices to platform."""
_async_setup_entities(devices, async_add_entities)

disp = async_dispatcher_connect(hass, VS_DISCOVERY.format(VS_FANS), async_discover)
hass.data[DOMAIN][VS_DISPATCHERS].append(disp)

_async_setup_entities(hass.data[DOMAIN][VS_FANS], async_add_entities)
return True
Copy link
Member

Choose a reason for hiding this comment

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

Nothing is checking this return value.



@callback
def _async_setup_entities(devices, async_add_entities):
"""Check if device is online and add entity."""
dev_list = []
for dev in devices:
if DEV_TYPE_TO_HA.get(dev.device_type) == "fan":
dev_list.append(VeSyncFanHA(dev))
else:
_LOGGER.warning(
"%s - Unknown device type - %s", dev.device_name, dev.device_type
)
continue

async_add_entities(dev_list, update_before_add=True)


class VeSyncFanHA(VeSyncDevice, FanEntity):
"""Representation of a VeSync fan."""

def __init__(self, fan):
"""Initialize the VeSync fan device."""
super().__init__(fan)
self.smartfan = fan

@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_SET_SPEED

@property
def is_on(self):
"""Return True if device is on."""
return self.smartfan.device_status == "on"

@property
def speed(self):
"""Return the current speed."""
if self.smartfan.mode == "auto":
return "auto"
if self.smartfan.mode == "manual":
current_level = self.smartfan.fan_level
if current_level is not None:
return FAN_SPEEDS[current_level]
return None
TheGardenMonkey marked this conversation as resolved.
Show resolved Hide resolved

@property
def speed_list(self):
"""Get the list of available speeds."""
return FAN_SPEEDS

@property
def unique_info(self):
"""Return the ID of this fan."""
return self.smartfan.uuid

@property
def name(self):
"""Return the name of the fan."""
return self.smartfan.device_name

@property
def device_state_attributes(self):
"""Return the state attributes of the fan."""
attr = {}
attr["mode"] = self.smartfan.mode
attr["active_time"] = self.smartfan.active_time
attr["filter_life"] = self.smartfan.filter_life
attr["air_quality"] = self.smartfan.air_quality
attr["screen_status"] = self.smartfan.screen_status
return attr
TheGardenMonkey marked this conversation as resolved.
Show resolved Hide resolved

def set_speed(self, speed):
"""Sets the speed of the device."""
TheGardenMonkey marked this conversation as resolved.
Show resolved Hide resolved
if speed is None or speed == "auto":
TheGardenMonkey marked this conversation as resolved.
Show resolved Hide resolved
self.smartfan.auto_mode()
else:
self.smartfan.manual_mode()
self.smartfan.change_fan_speed(FAN_SPEEDS.index(speed))

bdraco marked this conversation as resolved.
Show resolved Hide resolved
def turn_on(self, speed, **kwargs):
"""Turn the device on."""
self.smartfan.turn_on()
if speed is None or speed == "auto":
self.smartfan.auto_mode()
else:
self.smartfan.manual_mode()
self.smartfan.change_fan_speed(FAN_SPEEDS.index(speed))