mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add a Fan component and support for an Insteon Hub Fan (#2964)
* Fan component and service definitions * Insteon Hub fan support
This commit is contained in:
parent
17631cd728
commit
826ec9b9d7
224
homeassistant/components/fan/__init__.py
Normal file
224
homeassistant/components/fan/__init__.py
Normal file
@ -0,0 +1,224 @@
|
||||
"""
|
||||
Provides functionality to interact with fans.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/fan/
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import group
|
||||
from homeassistant.config import load_yaml_config_file
|
||||
from homeassistant.const import (
|
||||
STATE_OFF, SERVICE_TURN_ON,
|
||||
SERVICE_TURN_OFF, ATTR_ENTITY_ID)
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
|
||||
DOMAIN = 'fan'
|
||||
SCAN_INTERVAL = 30
|
||||
|
||||
GROUP_NAME_ALL_FANS = 'all fans'
|
||||
ENTITY_ID_ALL_FANS = group.ENTITY_ID_FORMAT.format(GROUP_NAME_ALL_FANS)
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + '.{}'
|
||||
|
||||
# Bitfield of features supported by the fan entity
|
||||
ATTR_SUPPORTED_FEATURES = 'supported_features'
|
||||
SUPPORT_SET_SPEED = 1
|
||||
SUPPORT_OSCILLATE = 2
|
||||
|
||||
SERVICE_SET_SPEED = 'set_speed'
|
||||
SERVICE_OSCILLATE = 'oscillate'
|
||||
|
||||
SPEED_OFF = 'off'
|
||||
SPEED_LOW = 'low'
|
||||
SPEED_MED = 'med'
|
||||
SPEED_HIGH = 'high'
|
||||
|
||||
ATTR_SPEED = 'speed'
|
||||
ATTR_SPEED_LIST = 'speed_list'
|
||||
ATTR_OSCILLATE = 'oscillate'
|
||||
|
||||
PROP_TO_ATTR = {
|
||||
'speed': ATTR_SPEED,
|
||||
'speed_list': ATTR_SPEED_LIST,
|
||||
'oscillate': ATTR_OSCILLATE,
|
||||
'supported_features': ATTR_SUPPORTED_FEATURES,
|
||||
} # type: dict
|
||||
|
||||
FAN_SET_SPEED_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Required(ATTR_SPEED): cv.string
|
||||
}) # type: dict
|
||||
|
||||
FAN_TURN_ON_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Optional(ATTR_SPEED): cv.string
|
||||
}) # type: dict
|
||||
|
||||
FAN_TURN_OFF_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_ids
|
||||
}) # type: dict
|
||||
|
||||
FAN_OSCILLATE_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Required(ATTR_OSCILLATE): cv.boolean
|
||||
}) # type: dict
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def is_on(hass, entity_id: str=None) -> bool:
|
||||
"""Return if the fans are on based on the statemachine."""
|
||||
entity_id = entity_id or ENTITY_ID_ALL_FANS
|
||||
return not hass.states.is_state(entity_id, STATE_OFF)
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
def turn_on(hass, entity_id: str=None, speed: str=None) -> None:
|
||||
"""Turn all or specified fan on."""
|
||||
data = {
|
||||
key: value for key, value in [
|
||||
(ATTR_ENTITY_ID, entity_id),
|
||||
(ATTR_SPEED, speed),
|
||||
] if value is not None
|
||||
}
|
||||
|
||||
hass.services.call(DOMAIN, SERVICE_TURN_ON, data)
|
||||
|
||||
|
||||
def turn_off(hass, entity_id: str=None) -> None:
|
||||
"""Turn all or specified fan off."""
|
||||
data = {
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
}
|
||||
|
||||
hass.services.call(DOMAIN, SERVICE_TURN_OFF, data)
|
||||
|
||||
|
||||
def oscillate(hass, entity_id: str=None, should_oscillate: bool=True) -> None:
|
||||
"""Set oscillation on all or specified fan."""
|
||||
data = {
|
||||
key: value for key, value in [
|
||||
(ATTR_ENTITY_ID, entity_id),
|
||||
(ATTR_OSCILLATE, should_oscillate),
|
||||
] if value is not None
|
||||
}
|
||||
|
||||
hass.services.call(DOMAIN, SERVICE_OSCILLATE, data)
|
||||
|
||||
|
||||
def set_speed(hass, entity_id: str=None, speed: str=None) -> None:
|
||||
"""Set speed for all or specified fan."""
|
||||
data = {
|
||||
key: value for key, value in [
|
||||
(ATTR_ENTITY_ID, entity_id),
|
||||
(ATTR_SPEED, speed),
|
||||
] if value is not None
|
||||
}
|
||||
|
||||
hass.services.call(DOMAIN, SERVICE_SET_SPEED, data)
|
||||
|
||||
|
||||
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
||||
def setup(hass, config: dict) -> None:
|
||||
"""Expose fan control via statemachine and services."""
|
||||
component = EntityComponent(
|
||||
_LOGGER, DOMAIN, hass, SCAN_INTERVAL, GROUP_NAME_ALL_FANS)
|
||||
component.setup(config)
|
||||
|
||||
def handle_fan_service(service: str) -> None:
|
||||
"""Hande service call for fans."""
|
||||
# Get the validated data
|
||||
params = service.data.copy()
|
||||
|
||||
# Convert the entity ids to valid fan ids
|
||||
target_fans = component.extract_from_service(service)
|
||||
params.pop(ATTR_ENTITY_ID, None)
|
||||
|
||||
service_fun = None
|
||||
for service_def in [SERVICE_TURN_ON, SERVICE_TURN_OFF,
|
||||
SERVICE_SET_SPEED, SERVICE_OSCILLATE]:
|
||||
if service_def == service.service:
|
||||
service_fun = service_def
|
||||
break
|
||||
|
||||
if service_fun:
|
||||
for fan in target_fans:
|
||||
getattr(fan, service_fun)(**params)
|
||||
|
||||
for fan in target_fans:
|
||||
if fan.should_poll:
|
||||
fan.update_ha_state(True)
|
||||
return
|
||||
|
||||
# Listen for fan service calls.
|
||||
descriptions = load_yaml_config_file(
|
||||
os.path.join(os.path.dirname(__file__), 'services.yaml'))
|
||||
hass.services.register(DOMAIN, SERVICE_TURN_ON, handle_fan_service,
|
||||
descriptions.get(SERVICE_TURN_ON),
|
||||
schema=FAN_TURN_ON_SCHEMA)
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_TURN_OFF, handle_fan_service,
|
||||
descriptions.get(SERVICE_TURN_OFF),
|
||||
schema=FAN_TURN_OFF_SCHEMA)
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_SET_SPEED, handle_fan_service,
|
||||
descriptions.get(SERVICE_SET_SPEED),
|
||||
schema=FAN_SET_SPEED_SCHEMA)
|
||||
|
||||
hass.services.register(DOMAIN, SERVICE_OSCILLATE, handle_fan_service,
|
||||
descriptions.get(SERVICE_OSCILLATE),
|
||||
schema=FAN_OSCILLATE_SCHEMA)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class FanEntity(Entity):
|
||||
"""Representation of a fan."""
|
||||
|
||||
# pylint: disable=no-self-use, abstract-method
|
||||
|
||||
def set_speed(self: Entity, speed: str) -> None:
|
||||
"""Set the speed of the fan."""
|
||||
pass
|
||||
|
||||
def turn_on(self: Entity, speed: str=None) -> None:
|
||||
"""Turn on the fan."""
|
||||
pass
|
||||
|
||||
def turn_off(self: Entity) -> None:
|
||||
"""Turn off the fan."""
|
||||
pass
|
||||
|
||||
def oscillate(self: Entity) -> None:
|
||||
"""Oscillate the fan."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def speed_list(self: Entity) -> list:
|
||||
"""Get the list of available speeds."""
|
||||
return []
|
||||
|
||||
@property
|
||||
def state_attributes(self: Entity) -> dict:
|
||||
"""Return optional state attributes."""
|
||||
data = {} # type: dict
|
||||
|
||||
for prop, attr in PROP_TO_ATTR.items():
|
||||
value = getattr(self, prop)
|
||||
if value is not None:
|
||||
data[attr] = value
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
def supported_features(self: Entity) -> int:
|
||||
"""Flag supported features."""
|
||||
return 0
|
71
homeassistant/components/fan/insteon_hub.py
Normal file
71
homeassistant/components/fan/insteon_hub.py
Normal file
@ -0,0 +1,71 @@
|
||||
"""
|
||||
Support for Insteon FanLinc.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/fan.insteon/
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from homeassistant.components.fan import (FanEntity, SUPPORT_SET_SPEED,
|
||||
SPEED_OFF, SPEED_LOW, SPEED_MED,
|
||||
SPEED_HIGH)
|
||||
from homeassistant.components.insteon_hub import (InsteonDevice, INSTEON,
|
||||
filter_devices)
|
||||
from homeassistant.const import STATE_UNKNOWN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEVICE_CATEGORIES = [
|
||||
{
|
||||
'DevCat': 1,
|
||||
'SubCat': [46]
|
||||
}
|
||||
]
|
||||
|
||||
DEPENDENCIES = ['insteon_hub']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Insteon Hub fan platform."""
|
||||
devs = []
|
||||
for device in filter_devices(INSTEON.devices, DEVICE_CATEGORIES):
|
||||
devs.append(InsteonFanDevice(device))
|
||||
add_devices(devs)
|
||||
|
||||
|
||||
class InsteonFanDevice(InsteonDevice, FanEntity):
|
||||
"""Represet an insteon fan device."""
|
||||
|
||||
def __init__(self, node: object) -> None:
|
||||
"""Initialize the device."""
|
||||
super(InsteonFanDevice, self).__init__(node)
|
||||
self.speed = STATE_UNKNOWN # Insteon hub can't get state via REST
|
||||
|
||||
def turn_on(self, speed: str=None):
|
||||
"""Turn the fan on."""
|
||||
self.set_speed(speed if speed else SPEED_MED)
|
||||
|
||||
def turn_off(self):
|
||||
"""Turn the fan off."""
|
||||
self.set_speed(SPEED_OFF)
|
||||
|
||||
def set_speed(self, speed: str) -> None:
|
||||
"""Set the fan speed."""
|
||||
if self._send_command('fan', payload={'speed', speed}):
|
||||
self.speed = speed
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Get the supported features for device."""
|
||||
return SUPPORT_SET_SPEED
|
||||
|
||||
@property
|
||||
def speed_list(self) -> list:
|
||||
"""Get the available speeds for the fan."""
|
||||
return [SPEED_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH]
|
||||
|
||||
@property
|
||||
def state(self) -> str:
|
||||
"""Get the current device state."""
|
||||
return self.speed
|
45
homeassistant/components/fan/services.yaml
Normal file
45
homeassistant/components/fan/services.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
# Describes the format for available fan services
|
||||
|
||||
set_speed:
|
||||
description: Sets fan speed
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of the entities to set
|
||||
example: 'fan.living_room'
|
||||
|
||||
speed:
|
||||
description: Speed setting
|
||||
example: 'low'
|
||||
|
||||
turn_on:
|
||||
description: Turns fan on
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Names(s) of the entities to turn on
|
||||
example: 'fan.living_room'
|
||||
|
||||
speed:
|
||||
description: Speed setting
|
||||
example: 'high'
|
||||
|
||||
turn_off:
|
||||
description: Turns fan off
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Names(s) of the entities to turn off
|
||||
example: 'fan.living_room'
|
||||
|
||||
oscillate:
|
||||
description: Oscillates the fan
|
||||
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of the entities to oscillate
|
||||
example: 'fan.desk_fan'
|
||||
|
||||
oscillate:
|
||||
description: Flag to turn on/off oscillation
|
||||
example: True
|
@ -8,37 +8,93 @@ import logging
|
||||
|
||||
from homeassistant.const import CONF_API_KEY, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.helpers import validate_config, discovery
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
DOMAIN = 'insteon_hub' # type: str
|
||||
REQUIREMENTS = ['insteon_hub==0.5.0'] # type: list
|
||||
INSTEON = None # type: Insteon
|
||||
DEVCAT = 'DevCat' # type: str
|
||||
SUBCAT = 'SubCat' # type: str
|
||||
DEVICE_CLASSES = ['light', 'fan'] # type: list
|
||||
|
||||
DOMAIN = "insteon_hub"
|
||||
REQUIREMENTS = ['insteon_hub==0.4.5']
|
||||
INSTEON = None
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup Insteon Hub component.
|
||||
def _is_successful(response: dict) -> bool:
|
||||
"""Check http response for successful status."""
|
||||
return 'status' in response and response['status'] == 'succeeded'
|
||||
|
||||
This will automatically import associated lights.
|
||||
"""
|
||||
|
||||
def filter_devices(devices: list, categories: list) -> list:
|
||||
"""Filter insteon device list by category/subcategory."""
|
||||
categories = (categories
|
||||
if isinstance(categories, list)
|
||||
else [categories])
|
||||
matching_devices = []
|
||||
for device in devices:
|
||||
if any(
|
||||
device.DevCat == c[DEVCAT] and
|
||||
(SUBCAT not in c or device.SubCat in c[SUBCAT])
|
||||
for c in categories):
|
||||
matching_devices.append(device)
|
||||
return matching_devices
|
||||
|
||||
|
||||
def setup(hass, config: dict) -> bool:
|
||||
"""Setup Insteon Hub component."""
|
||||
if not validate_config(
|
||||
config,
|
||||
{DOMAIN: [CONF_USERNAME, CONF_PASSWORD, CONF_API_KEY]},
|
||||
_LOGGER):
|
||||
return False
|
||||
|
||||
import insteon
|
||||
from insteon import Insteon
|
||||
|
||||
username = config[DOMAIN][CONF_USERNAME]
|
||||
password = config[DOMAIN][CONF_PASSWORD]
|
||||
api_key = config[DOMAIN][CONF_API_KEY]
|
||||
|
||||
global INSTEON
|
||||
INSTEON = insteon.Insteon(username, password, api_key)
|
||||
INSTEON = Insteon(username, password, api_key)
|
||||
|
||||
if INSTEON is None:
|
||||
_LOGGER.error("Could not connect to Insteon service.")
|
||||
_LOGGER.error('Could not connect to Insteon service.')
|
||||
return
|
||||
|
||||
discovery.load_platform(hass, 'light', DOMAIN, {}, config)
|
||||
|
||||
for device_class in DEVICE_CLASSES:
|
||||
discovery.load_platform(hass, device_class, DOMAIN, {}, config)
|
||||
return True
|
||||
|
||||
|
||||
class InsteonDevice(Entity):
|
||||
"""Represents an insteon device."""
|
||||
|
||||
def __init__(self: Entity, node: object) -> None:
|
||||
"""Initialize the insteon device."""
|
||||
self._node = node
|
||||
|
||||
def update(self: Entity) -> None:
|
||||
"""Update state of the device."""
|
||||
pass
|
||||
|
||||
@property
|
||||
def name(self: Entity) -> str:
|
||||
"""Name of the insteon device."""
|
||||
return self._node.DeviceName
|
||||
|
||||
@property
|
||||
def unique_id(self: Entity) -> str:
|
||||
"""Unique identifier for the device."""
|
||||
return self._node.DeviceID
|
||||
|
||||
@property
|
||||
def supported_features(self: Entity) -> int:
|
||||
"""Supported feature flags."""
|
||||
return 0
|
||||
|
||||
def _send_command(self: Entity, command: str, level: int=None,
|
||||
payload: dict=None) -> bool:
|
||||
"""Send command to insteon device."""
|
||||
resp = self._node.send_command(command, payload=payload, level=level,
|
||||
wait=True)
|
||||
return _is_successful(resp)
|
||||
|
@ -4,74 +4,76 @@ Support for Insteon Hub lights.
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/insteon_hub/
|
||||
"""
|
||||
from homeassistant.components.insteon_hub import INSTEON
|
||||
from homeassistant.components.insteon_hub import (INSTEON, InsteonDevice)
|
||||
from homeassistant.components.light import (ATTR_BRIGHTNESS,
|
||||
SUPPORT_BRIGHTNESS, Light)
|
||||
|
||||
SUPPORT_INSTEON_HUB = SUPPORT_BRIGHTNESS
|
||||
|
||||
DEPENDENCIES = ['insteon_hub']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Insteon Hub light platform."""
|
||||
devs = []
|
||||
for device in INSTEON.devices:
|
||||
if device.DeviceCategory == "Switched Lighting Control":
|
||||
devs.append(InsteonToggleDevice(device))
|
||||
devs.append(InsteonLightDevice(device))
|
||||
if device.DeviceCategory == "Dimmable Lighting Control":
|
||||
devs.append(InsteonToggleDevice(device))
|
||||
devs.append(InsteonDimmableDevice(device))
|
||||
add_devices(devs)
|
||||
|
||||
|
||||
class InsteonToggleDevice(Light):
|
||||
"""An abstract Class for an Insteon node."""
|
||||
class InsteonLightDevice(InsteonDevice, Light):
|
||||
"""A representation of a light device."""
|
||||
|
||||
def __init__(self, node):
|
||||
def __init__(self, node: object) -> None:
|
||||
"""Initialize the device."""
|
||||
self.node = node
|
||||
super(InsteonLightDevice, self).__init__(node)
|
||||
self._value = 0
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
return self.node.DeviceName
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this insteon node."""
|
||||
return self.node.DeviceID
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return self._value / 100 * 255
|
||||
|
||||
def update(self):
|
||||
"""Update state of the sensor."""
|
||||
resp = self.node.send_command('get_status', wait=True)
|
||||
def update(self) -> None:
|
||||
"""Update state of the device."""
|
||||
resp = self._node.send_command('get_status', wait=True)
|
||||
try:
|
||||
self._value = resp['response']['level']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
def is_on(self) -> None:
|
||||
"""Return the boolean response if the node is on."""
|
||||
return self._value != 0
|
||||
|
||||
def turn_on(self, **kwargs) -> None:
|
||||
"""Turn device on."""
|
||||
if self._send_command('on'):
|
||||
self._value = 100
|
||||
|
||||
def turn_off(self, **kwargs) -> None:
|
||||
"""Turn device off."""
|
||||
if self._send_command('off'):
|
||||
self._value = 0
|
||||
|
||||
|
||||
class InsteonDimmableDevice(InsteonLightDevice):
|
||||
"""A representation for a dimmable device."""
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
def brightness(self) -> int:
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return round(self._value / 100 * 255, 0) # type: int
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_INSTEON_HUB
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
def turn_on(self, **kwargs) -> None:
|
||||
"""Turn device on."""
|
||||
level = 100 # type: int
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
self._value = kwargs[ATTR_BRIGHTNESS] / 255 * 100
|
||||
self.node.send_command('on', self._value)
|
||||
else:
|
||||
self._value = 100
|
||||
self.node.send_command('on')
|
||||
level = round(kwargs[ATTR_BRIGHTNESS] / 255 * 100, 0) # type: int
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn device off."""
|
||||
self.node.send_command('off')
|
||||
if self._send_command('on', level=level):
|
||||
self._value = level
|
||||
|
@ -204,7 +204,7 @@ https://github.com/wokar/pylgnetcast/archive/v0.2.0.zip#pylgnetcast==0.2.0
|
||||
influxdb==3.0.0
|
||||
|
||||
# homeassistant.components.insteon_hub
|
||||
insteon_hub==0.4.5
|
||||
insteon_hub==0.5.0
|
||||
|
||||
# homeassistant.components.media_player.kodi
|
||||
jsonrpc-requests==0.3
|
||||
|
1
tests/components/fan/__init__.py
Normal file
1
tests/components/fan/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Test fan component plaforms."""
|
76
tests/components/fan/test_insteon_hub.py
Normal file
76
tests/components/fan/test_insteon_hub.py
Normal file
@ -0,0 +1,76 @@
|
||||
"""Tests for the insteon hub fan platform."""
|
||||
import unittest
|
||||
|
||||
from homeassistant.const import STATE_OFF, STATE_UNKNOWN
|
||||
from homeassistant.components.fan import (SPEED_LOW, SPEED_MED, SPEED_HIGH)
|
||||
from homeassistant.components.fan.insteon_hub import (InsteonFanDevice,
|
||||
SUPPORT_SET_SPEED)
|
||||
|
||||
|
||||
class Node(object):
|
||||
"""Fake insteon node."""
|
||||
|
||||
def __init__(self, name, id, dev_cat, sub_cat):
|
||||
"""Initialize fake insteon node."""
|
||||
self.DeviceName = name
|
||||
self.DeviceID = id
|
||||
self.DevCat = dev_cat
|
||||
self.SubCat = sub_cat
|
||||
self.response = None
|
||||
|
||||
def send_command(self, command, payload, level, wait):
|
||||
"""Send fake command."""
|
||||
return self.response
|
||||
|
||||
|
||||
class TestInsteonHubFanDevice(unittest.TestCase):
|
||||
"""Test around insteon hub fan device methods."""
|
||||
|
||||
_NODE = Node('device', '12345', '1', '46')
|
||||
|
||||
def setUp(self):
|
||||
"""Initialize test data."""
|
||||
self._DEVICE = InsteonFanDevice(self._NODE)
|
||||
|
||||
def tearDown(self):
|
||||
"""Tear down test data."""
|
||||
self._DEVICE = None
|
||||
|
||||
def test_properties(self):
|
||||
"""Test basic properties."""
|
||||
self.assertEqual(self._NODE.DeviceName, self._DEVICE.name)
|
||||
self.assertEqual(self._NODE.DeviceID, self._DEVICE.unique_id)
|
||||
self.assertEqual(SUPPORT_SET_SPEED, self._DEVICE.supported_features)
|
||||
|
||||
for speed in [STATE_OFF, SPEED_LOW, SPEED_MED, SPEED_HIGH]:
|
||||
self.assertIn(speed, self._DEVICE.speed_list)
|
||||
|
||||
def test_turn_on(self):
|
||||
"""Test the turning on device."""
|
||||
self._NODE.response = {
|
||||
'status': 'succeeded'
|
||||
}
|
||||
self.assertEqual(STATE_UNKNOWN, self._DEVICE.state)
|
||||
self._DEVICE.turn_on()
|
||||
|
||||
self.assertNotEqual(STATE_OFF, self._DEVICE.state)
|
||||
|
||||
self._DEVICE.turn_on(SPEED_MED)
|
||||
|
||||
self.assertEqual(SPEED_MED, self._DEVICE.state)
|
||||
|
||||
self._NODE.response = {
|
||||
}
|
||||
self._DEVICE.turn_on(SPEED_HIGH)
|
||||
|
||||
self.assertNotEqual(SPEED_HIGH, self._DEVICE.state)
|
||||
|
||||
def test_turn_off(self):
|
||||
"""Test turning off device."""
|
||||
self._NODE.response = {
|
||||
'status': 'succeeded'
|
||||
}
|
||||
self.assertEqual(STATE_UNKNOWN, self._DEVICE.state)
|
||||
self._DEVICE.turn_off()
|
||||
|
||||
self.assertEqual(STATE_OFF, self._DEVICE.state)
|
Loading…
x
Reference in New Issue
Block a user