Skip to content

Instantly share code, notes, and snippets.

@fake666
Last active June 24, 2020 08:58
Show Gist options
  • Save fake666/75c035c74b7be448dd9b3d1316ea6cfe to your computer and use it in GitHub Desktop.
Save fake666/75c035c74b7be448dd9b3d1316ea6cfe to your computer and use it in GitHub Desktop.
"""
A homeassistant switch component to enable or disable fritz! box call deflections / call forwarding
tr 064 needs to be enabled, and call deflections have to be pre-defined in the box's ui.
removing and adding calldeflections while homeassistant is running will break this ;-)
"""
import logging
import voluptuous as vol
from homeassistant.components.switch import (SwitchEntity, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_PASSWORD, CONF_USERNAME)
import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['fritzconnection==1.3.0']
_LOGGER = logging.getLogger(__name__)
DEFAULT_HOST = '169.254.1.1' # IP valid for all Fritz!Box routers
DEFAULT_PORT = 49000
ATTR_TRIGGER_NUMBER = 'trigger_number'
ATTR_TARGET_NUMBER = 'target_number'
ATTR_UID = 'uid'
SERVICE = 'X_AVM-DE_OnTel'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME, default=''): cv.string,
})
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Fritz!Box call deflection switch platform."""
host = config.get(CONF_HOST)
port = config.get(CONF_PORT)
username = config.get(CONF_USERNAME)
password = config.get(CONF_PASSWORD)
from lxml import etree
import fritzconnection as fc
fritz_connection = None;
if username != '':
fritz_connection = fc.FritzConnection(address=host, password=password, user=username, port=port)
else:
fritz_connection = fc.FritzConnection(address=host, password=password, port=port)
raw_deflections = fritz_connection.call_action(SERVICE, "GetDeflections")["NewDeflectionList"];
element_tree = etree.fromstring(raw_deflections);
switches = []
for element in element_tree.findall('Item'):
uid = element.find('DeflectionId').text
enabled = int(element.find('Enable').text)
from_number = element.find('Number').text
to_number = element.find('DeflectionToNumber').text
# forwardings without to_number are blocked numbers
if to_number is None:
continue
switches.append(FritzCallDeflectSwitch(hass=hass, fritz_connection=fritz_connection, uid=uid, from_number=from_number, to_number=to_number, enabled=enabled))
add_entities(switches);
class FritzCallDeflectSwitch(SwitchEntity):
"""Representation of a FRITZ! call deflection switch."""
def __init__(self, hass, fritz_connection, uid, from_number, to_number, enabled):
"""Initialize the switch."""
self._hass = hass;
self._fritz_connection = fritz_connection;
self._uid = uid;
self._from_number = from_number;
self._to_number = to_number;
self._enabled = enabled;
@property
def name(self):
"""Return the name of the FRITZ! call deflection (just the uid)"""
return "fritzdeflect_" + str(self._uid);
@property
def device_state_attributes(self):
"""Return the state attributes of the call deflection."""
attrs = {}
attrs[ATTR_TRIGGER_NUMBER] = "{}".format(self._from_number)
attrs[ATTR_TARGET_NUMBER] = "{}".format(self._to_number)
attrs[ATTR_UID] = "{}".format(self._uid);
return attrs
@property
def is_on(self):
"""Return true if switch is on."""
return self._enabled;
def turn_on(self, **kwargs):
"""Turn the switch on."""
myargs = {'NewDeflectionId': self._uid, 'NewEnable': 1 }
self._fritz_connection.call_action(SERVICE, "SetDeflectionEnable", **myargs);
def turn_off(self, **kwargs):
"""Turn the switch off."""
myargs = {'NewDeflectionId': self._uid, 'NewEnable': 0 }
self._fritz_connection.call_action(SERVICE, "SetDeflectionEnable", **myargs);
def update(self):
"""Get the latest data from the fritz box and update the state"""
kwargs = {'NewDeflectionId': self._uid }
try:
updated_dict = self._fritz_connection.call_action(SERVICE, "GetDeflection", **kwargs);
self._from_number = updated_dict['NewNumber'];
self._to_number = updated_dict['NewDeflectionToNumber'];
self._enabled = int(updated_dict['NewEnable']);
except TypeError:
pass
@fake666
Copy link
Author

fake666 commented Nov 13, 2018

usage: drop in custom_compoments/switch, add (in configuration.yaml):

switch:
platform: fritzbox_calldeflection
host: 192.168.178.1 (optional)
username: myuser (optional)
password: !secret fb_pass

restart HA

@Krocko
Copy link

Krocko commented Dec 20, 2019

Is this still working?
I got the following error:

Platform error switch.fritzbox_calldeflection - Integration 'fritzbox_calldeflection' not found.

@fake666
Copy link
Author

fake666 commented Dec 20, 2019

hi, the layout for custom components changed a bit a while back, insteaf of custom_components/switch it now has to be placed in

custom_components/fritzbox_calldeflection/switch.py

you may also need to create an empty __init__.py in the same directory.

@Krocko
Copy link

Krocko commented Dec 20, 2019

Thank you for your help. I have done this but I get the same error message.

@Krocko
Copy link

Krocko commented Dec 20, 2019

Ok. Now I am one step closer.
I renamed folder an file and changed the configuration.yaml entry

fritzbox_calldeflection:
host: 192.168.178.1 (optional)
username: myuser (optional)
password: !secret fb_pass

But now I get the following error:
Setup failed for fritzbox_calldeflection: No setup function defined.

@fake666
Copy link
Author

fake666 commented Dec 20, 2019

here's my config:

switch:
   - platform: fritzbox_calldeflection
     host: 192.168.178.1
     password: !secret fb_password

@Krocko
Copy link

Krocko commented Dec 20, 2019

If I change my config like yours, home assistant don‘t start anymore.
Did you have other files in /custom_components/fritzbox_calldeflection?

@fake666
Copy link
Author

fake666 commented Dec 21, 2019

no, just switch.py and the empty __init__.py.
do any other custom components work for you? does homeassistant have write permission on this directory?

@Krocko
Copy link

Krocko commented Dec 21, 2019

Yes i have other custom components and they are working.
The permission of the folder and files are 777.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment