From e831a2705e5ed2c240af3ba9e6a6acc8f110c960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Bastian=20P=C3=B6ttner?= <bastian@poettner.de> Date: Fri, 3 Feb 2017 08:29:18 +0100 Subject: [PATCH] Add support for FRITZ!DECT wireless switches based on fritzhome (#5541) --- .coveragerc | 1 + homeassistant/components/switch/fritzdect.py | 165 +++++++++++++++++++ requirements_all.txt | 3 + 3 files changed, 169 insertions(+) create mode 100644 homeassistant/components/switch/fritzdect.py diff --git a/.coveragerc b/.coveragerc index dbccf546c36..093b0e4d526 100644 --- a/.coveragerc +++ b/.coveragerc @@ -367,6 +367,7 @@ omit = homeassistant/components/switch/digitalloggers.py homeassistant/components/switch/dlink.py homeassistant/components/switch/edimax.py + homeassistant/components/switch/fritzdect.py homeassistant/components/switch/hdmi_cec.py homeassistant/components/switch/hikvisioncam.py homeassistant/components/switch/hook.py diff --git a/homeassistant/components/switch/fritzdect.py b/homeassistant/components/switch/fritzdect.py new file mode 100644 index 00000000000..fc185c9f6a3 --- /dev/null +++ b/homeassistant/components/switch/fritzdect.py @@ -0,0 +1,165 @@ +""" +Support for FRITZ!DECT Switches. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/switch.fritzdect/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.const import ( + CONF_HOST, CONF_PASSWORD, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv +from homeassistant.const import TEMP_CELSIUS, STATE_UNKNOWN + +REQUIREMENTS = ['fritzhome==1.0.2'] + +_LOGGER = logging.getLogger(__name__) + +# Standard Fritz Box IP +DEFAULT_HOST = 'fritz.box' + +ATTR_CURRENT_CONSUMPTION = 'Current Consumption' +ATTR_CURRENT_CONSUMPTION_UNIT = 'W' + +ATTR_TOTAL_CONSUMPTION = 'Total Consumption' +ATTR_TOTAL_CONSUMPTION_UNIT = 'kWh' + +ATTR_TEMPERATURE = 'Temperature' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Add all switches connected to Fritz Box.""" + from fritzhome.fritz import FritzBox + + host = config.get(CONF_HOST) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + + # Hack: fritzhome only throws Exception. To prevent pylint from + # complaining, we disable the warning here: + # pylint: disable=W0703 + + # Log into Fritz Box + fritz = FritzBox(host, username, password) + try: + fritz.login() + except Exception: + _LOGGER.error("Login to Fritz!Box failed") + return + + # Add all actors to hass + for actor in fritz.get_actors(): + # Only add devices that support switching + if actor.has_switch: + data = FritzDectSwitchData(fritz, actor.actor_id) + add_devices([FritzDectSwitch(hass, data, actor.name)], True) + + +class FritzDectSwitch(SwitchDevice): + """Representation of a FRITZ!DECT switch.""" + + def __init__(self, hass, data, name): + """Initialize the switch.""" + self.units = hass.config.units + self.data = data + self._name = name + + @property + def name(self): + """Return the name of the FRITZ!DECT switch, if any.""" + return self._name + + @property + def device_state_attributes(self): + """Return the state attributes of the device.""" + attrs = {} + + if self.data.has_powermeter and \ + self.data.current_consumption != STATE_UNKNOWN and \ + self.data.total_consumption != STATE_UNKNOWN: + attrs[ATTR_CURRENT_CONSUMPTION] = "%.1f %s" % \ + (self.data.current_consumption, ATTR_CURRENT_CONSUMPTION_UNIT) + attrs[ATTR_TOTAL_CONSUMPTION] = "%.3f %s" % \ + (self.data.total_consumption, ATTR_TOTAL_CONSUMPTION_UNIT) + + if self.data.has_temperature and \ + self.data.temperature != STATE_UNKNOWN: + attrs[ATTR_TEMPERATURE] = "%.1f %s" % \ + (self.units.temperature(self.data.temperature, TEMP_CELSIUS), + self.units.temperature_unit) + + return attrs + + @property + def current_power_watt(self): + """Return the current power usage in Watt.""" + try: + return float(self.data.current_consumption) + except ValueError: + return None + + @property + def is_on(self): + """Return true if switch is on.""" + return self.data.state + + def turn_on(self, **kwargs): + """Turn the switch on.""" + actor = self.data.fritz.get_actor_by_ain(self.data.ain) + actor.switch_on() + + def turn_off(self): + """Turn the switch off.""" + actor = self.data.fritz.get_actor_by_ain(self.data.ain) + actor.switch_off() + + def update(self): + """Get the latest data from the fritz box and updates the states.""" + self.data.update() + + +class FritzDectSwitchData(object): + """Get the latest data from the fritz box.""" + + def __init__(self, fritz, ain): + """Initialize the data object.""" + self.fritz = fritz + self.ain = ain + self.state = STATE_UNKNOWN + self.temperature = STATE_UNKNOWN + self.current_consumption = STATE_UNKNOWN + self.total_consumption = STATE_UNKNOWN + self.has_switch = STATE_UNKNOWN + self.has_temperature = STATE_UNKNOWN + self.has_powermeter = STATE_UNKNOWN + + def update(self): + """Get the latest data from the fritz box.""" + from requests.exceptions import RequestException + + try: + actor = self.fritz.get_actor_by_ain(self.ain) + self.state = actor.get_state() + except RequestException: + _LOGGER.error("Request to actor failed") + self.state = STATE_UNKNOWN + self.temperature = STATE_UNKNOWN + self.current_consumption = STATE_UNKNOWN + self.total_consumption = STATE_UNKNOWN + return + + self.temperature = actor.temperature + self.current_consumption = (actor.get_power() or 0.0) / 1000 + self.total_consumption = (actor.get_energy() or 0.0) / 100000 + self.has_switch = actor.has_switch + self.has_temperature = actor.has_temperature + self.has_powermeter = actor.has_powermeter diff --git a/requirements_all.txt b/requirements_all.txt index d2d19cc23b0..957a1c3b75c 100755 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -154,6 +154,9 @@ freesms==0.1.1 # homeassistant.components.device_tracker.fritz # fritzconnection==0.6 +# homeassistant.components.switch.fritzdect +fritzhome==1.0.2 + # homeassistant.components.conversation fuzzywuzzy==0.14.0 -- GitLab