Skip to content
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ omit =
homeassistant/components/light/decora_wifi.py
homeassistant/components/light/decora.py
homeassistant/components/light/flux_led.py
homeassistant/components/light/futurenow.py
homeassistant/components/light/greenwave.py
homeassistant/components/light/hue.py
homeassistant/components/light/hyperion.py
Expand Down
141 changes: 141 additions & 0 deletions homeassistant/components/light/futurenow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""
Support for FutureNow Ethernet unit outputs as Lights.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.futurenow/
"""

import logging
import time

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

'time' imported but unused


import voluptuous as vol

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a blank line between standard library and 3rd party imports.


from homeassistant.const import (
CONF_NAME, CONF_HOST, CONF_PORT, CONF_DEVICES)
from homeassistant.components.light import (
ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, Light,
PLATFORM_SCHEMA)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['pyfnip==0.1']

_LOGGER = logging.getLogger(__name__)

CONF_DRIVER = 'driver'
CONF_DRIVER_FNIP6X10AD = 'FNIP6x10ad'
CONF_DRIVER_FNIP8X10A = 'FNIP8x10a'
CONF_DRIVER_TYPES = [CONF_DRIVER_FNIP6X10AD, CONF_DRIVER_FNIP8X10A]

DEVICE_SCHEMA = vol.Schema({
vol.Required(CONF_NAME): cv.string,
vol.Optional('dimmable', default=False): cv.boolean,
})

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_DRIVER): vol.In(CONF_DRIVER_TYPES),
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PORT): cv.port,
vol.Required(CONF_DEVICES): {cv.string: DEVICE_SCHEMA},
})


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the light platform for each FutureNow unit."""
lights = []
for channel, device_config in config[CONF_DEVICES].items():
device = {}
device['name'] = device_config[CONF_NAME]
device['dimmable'] = True if device_config['dimmable'] is True else False

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (81 > 79 characters)

device['channel'] = channel
device['driver'] = config[CONF_DRIVER]
device['host'] = config[CONF_HOST]
device['port'] = config[CONF_PORT]
lights.append(FutureNowLight(device))

add_devices(lights, True)


def to_futurenow_level(level):
"""Convert the given HASS light level (0-255) to FutureNow (0-100)."""
return int((level * 100) / 255)


def to_hass_level(level):
"""Convert the given FutureNow (0-100) light level to HASS (0-255)."""
return int((level * 255) / 100)


class FutureNowLight(Light):
"""Representation of an FutureNow light."""

def __init__(self, device):
"""Initialize the light."""
import pyfnip

self._name = device['name']
self._dimmable = device['dimmable']
self._channel = device['channel']
self._brightness = None
self._state = None
self._skip_update = False

if device['driver'] == CONF_DRIVER_FNIP6X10AD:
self._light = pyfnip.FNIP6x2adOutput(device['host'],
device['port'],
self._channel)
if device['driver'] == CONF_DRIVER_FNIP8X10A:
self._light = pyfnip.FNIP8x10aOutput(device['host'],
device['port'],
self._channel)

@property
def name(self):
"""Return the name of the device if any."""
return self._name

@property
def is_on(self):
"""Return true if device is on."""
return self._state

@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
return self._brightness

@property
def supported_features(self):
"""Flag supported features."""
if self._dimmable:
return SUPPORT_BRIGHTNESS
return 0

def turn_on(self, **kwargs):
"""Turn the light on."""
level = kwargs.get(ATTR_BRIGHTNESS, 255) if self._dimmable else 255
self._light.turn_on(to_futurenow_level(level))
self._brightness = level
self._state = True
self._skip_update = True

def turn_off(self, **kwargs):
"""Turn the light off."""
self._light.turn_off()
self._brightness = 0
self._state = False
self._skip_update = True

def update(self):
"""Fetch new state data for this light."""
if self._skip_update:
self._skip_update = False
return

state = int(self._light.is_on())
if state:
self._state = True

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here we can simplify to:

self._state = bool(state)

And then we can remove the if/else block.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for a great suggestion, it indeed seems to work! My initial thinking was probably over-complex, given that the two different FutureNow units return their state a bit differently and I wanted to use same code for all unit types when possible.

Returned state can be:

  • Dimmer module (FNIP-6x2AD) returns brightness 0-100 as state (0 is OFF).
  • Relay module (FNIP-8x10A) returns 0 is OFF, anything above 0 is ON.

self._brightness = to_hass_level(state)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should work both when state is 0 and above 0. We can move it out of the if state: block.

else:
if self._dimmable:
self._brightness = 0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we need this case.

self._state = False
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,9 @@ pyflexit==0.3
# homeassistant.components.binary_sensor.flic
pyflic-homeassistant==0.4.dev0

# homeassistant.components.light.futurenow
pyfnip==0.1

# homeassistant.components.fritzbox
pyfritzhome==0.3.7

Expand Down