homeassistant-config/custom_components/fhem/light.py

135 lines
4.7 KiB
Python

"""Support for lights from FHEM"""
import voluptuous as vol
import logging
from homeassistant.components.light import ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, LightEntity, ATTR_TRANSITION
from homeassistant.const import CONF_NAME
import homeassistant.helpers.config_validation as cv
from . import DATA_FHEM, device_error_reporting, CONF_FHEM_IDS
CONF_DIMMER = 'dimmer'
_LOGGER = logging.getLogger(__name__)
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_FHEM_IDS): vol.All(cv.ensure_list, [cv.string]),
vol.Optional(CONF_DIMMER, default=False): cv.boolean,
vol.Required(CONF_NAME): cv.string,
})
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up lights for KNX platform."""
connection = hass.data[DATA_FHEM]
light = FhemLight(connection, config[CONF_NAME], config[CONF_FHEM_IDS], dimmer=config[CONF_DIMMER])
for dev_id in config[CONF_FHEM_IDS]:
connection.register_device(dev_id, light)
async_add_entities([light])
class FhemLight(LightEntity):
def __init__(self, connection, name, ids, dimmer=False):
self._brightness = None
self.connection = connection
self._dimmer = dimmer
self._ids = ids
self._name = name
self._available = True
@property
def name(self):
return self._name
@property
def should_poll(self):
"""No polling needed."""
return False
@property
def available(self) -> bool:
return self._available and self.connection.connected
@property
def supported_features(self):
"""Flag supported features."""
flags = 0
if self._dimmer:
flags |= SUPPORT_BRIGHTNESS
return flags
@property
def brightness(self):
return self._brightness
@property
def is_on(self):
"""Return true if light is on."""
if self._brightness is not None:
return self._brightness > 0
else:
return None
def refresh(self):
self.connection.fhem_set(self._ids[0], 'statusRequest')
async def async_turn_on(self, **kwargs):
brightness = kwargs.get(ATTR_BRIGHTNESS, None)
transition_time = kwargs.get(ATTR_TRANSITION, 0.5)
if brightness is None:
brightness = 255
if self._dimmer:
# zero in the middle is the time until light is switched off,
# which is disabled here when passing 0
self.connection.fhem_set(self._ids[0], int(brightness / 255 * 100), 0, transition_time)
else:
self.connection.fhem_set(self._ids[0], 'on')
self._brightness = brightness
await self.async_update_ha_state()
async def async_turn_off(self, **kwargs):
self.connection.fhem_set(self._ids[0], 'off')
async def line_received(self, line):
if line.startswith('dim:'):
self._available = True
_, new_dim_state, new_level = line.split(':')
new_level = new_level.strip().lower()
new_dim_state = new_dim_state.strip().lower()
assert new_dim_state == 'stop' or new_dim_state == 'up' or new_dim_state == 'down'
if new_dim_state == 'stop':
if new_level == 'on':
self._brightness = 255
elif new_level == 'off':
self._brightness = 0
else:
new_level = int(float(new_level)) # first convert from string to floating point then truncate
assert 0 <= new_level <= 100
self._brightness = int(new_level / 100 * 255)
await self.async_update_ha_state()
elif line.startswith('level:') and not self._dimmer:
self._available = True
_, new_level = line.split(':')
new_level = new_level.strip().lower()
if new_level == 'on':
self._brightness = 255
elif new_level == 'off':
self._brightness = 0
else:
try:
new_level = int(float(new_level)) # first convert from string to floating point then truncate
assert 0 <= new_level <= 100
self._brightness = int(new_level / 100 * 255)
except ValueError:
pass
await self.async_update_ha_state()
elif line.startswith('ResndFail') or line.startswith('MISSING ACK'):
_LOGGER.warning(f"FHEM light {self.name} became unavailable: '{line}'")
self._available = False
await self.async_update_ha_state()
else:
device_error_reporting(self.hass, line, component_type="Light", component_name=self.entity_id)