Remove Tahoma component #29744 (#29745)

pull/29787/head
tetienne 2019-12-09 13:35:14 +01:00 committed by Pascal Vizeli
parent 4bb670cdf7
commit df74272ba6
9 changed files with 0 additions and 756 deletions

View File

@ -317,7 +317,6 @@ homeassistant/components/syncthru/* @nielstron
homeassistant/components/synology_srm/* @aerialls
homeassistant/components/syslog/* @fabaff
homeassistant/components/tado/* @michaelarnauts
homeassistant/components/tahoma/* @philklei
homeassistant/components/tautulli/* @ludeeus
homeassistant/components/tellduslive/* @fredrike
homeassistant/components/template/* @PhracturedBlue

View File

@ -1,140 +0,0 @@
"""Support for Tahoma devices."""
from collections import defaultdict
import logging
from requests.exceptions import RequestException
from tahoma_api import Action, TahomaApi
import voluptuous as vol
from homeassistant.const import CONF_EXCLUDE, CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.entity import Entity
_LOGGER = logging.getLogger(__name__)
DOMAIN = "tahoma"
TAHOMA_ID_FORMAT = "{}_{}"
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_EXCLUDE, default=[]): vol.All(
cv.ensure_list, [cv.string]
),
}
)
},
extra=vol.ALLOW_EXTRA,
)
TAHOMA_COMPONENTS = ["scene", "sensor", "cover", "switch", "binary_sensor"]
TAHOMA_TYPES = {
"io:ExteriorVenetianBlindIOComponent": "cover",
"io:HorizontalAwningIOComponent": "cover",
"io:LightIOSystemSensor": "sensor",
"io:OnOffIOComponent": "switch",
"io:OnOffLightIOComponent": "switch",
"io:RollerShutterGenericIOComponent": "cover",
"io:RollerShutterUnoIOComponent": "cover",
"io:RollerShutterVeluxIOComponent": "cover",
"io:RollerShutterWithLowSpeedManagementIOComponent": "cover",
"io:SomfyBasicContactIOSystemSensor": "sensor",
"io:SomfyContactIOSystemSensor": "sensor",
"io:VerticalExteriorAwningIOComponent": "cover",
"io:VerticalInteriorBlindVeluxIOComponent": "cover",
"io:WindowOpenerVeluxIOComponent": "cover",
"io:GarageOpenerIOComponent": "cover",
"io:DiscreteGarageOpenerIOComponent": "cover",
"rtds:RTDSContactSensor": "sensor",
"rtds:RTDSMotionSensor": "sensor",
"rtds:RTDSSmokeSensor": "smoke",
"rts:BlindRTSComponent": "cover",
"rts:CurtainRTSComponent": "cover",
"rts:DualCurtainRTSComponent": "cover",
"rts:ExteriorVenetianBlindRTSComponent": "cover",
"rts:GarageDoor4TRTSComponent": "switch",
"rts:RollerShutterRTSComponent": "cover",
"rts:VenetianBlindRTSComponent": "cover",
}
def setup(hass, config):
"""Activate Tahoma component."""
conf = config[DOMAIN]
username = conf.get(CONF_USERNAME)
password = conf.get(CONF_PASSWORD)
exclude = conf.get(CONF_EXCLUDE)
try:
api = TahomaApi(username, password)
except RequestException:
_LOGGER.exception("Error when trying to log in to the Tahoma API")
return False
try:
api.get_setup()
devices = api.get_devices()
scenes = api.get_action_groups()
except RequestException:
_LOGGER.exception("Error when getting devices from the Tahoma API")
return False
hass.data[DOMAIN] = {"controller": api, "devices": defaultdict(list), "scenes": []}
for device in devices:
_device = api.get_device(device)
if all(ext not in _device.type for ext in exclude):
device_type = map_tahoma_device(_device)
if device_type is None:
_LOGGER.warning(
"Unsupported type %s for Tahoma device %s",
_device.type,
_device.label,
)
continue
hass.data[DOMAIN]["devices"][device_type].append(_device)
for scene in scenes:
hass.data[DOMAIN]["scenes"].append(scene)
for component in TAHOMA_COMPONENTS:
discovery.load_platform(hass, component, DOMAIN, {}, config)
return True
def map_tahoma_device(tahoma_device):
"""Map Tahoma device types to Home Assistant components."""
return TAHOMA_TYPES.get(tahoma_device.type)
class TahomaDevice(Entity):
"""Representation of a Tahoma device entity."""
def __init__(self, tahoma_device, controller):
"""Initialize the device."""
self.tahoma_device = tahoma_device
self.controller = controller
self._name = self.tahoma_device.label
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def device_state_attributes(self):
"""Return the state attributes of the device."""
return {"tahoma_device_id": self.tahoma_device.url}
def apply_action(self, cmd_name, *args):
"""Apply Action to Device."""
action = Action(self.tahoma_device.url)
action.add_command(cmd_name, *args)
self.controller.apply_actions("HomeAssistant", [action])

View File

@ -1,95 +0,0 @@
"""Support for Tahoma binary sensors."""
from datetime import timedelta
import logging
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.const import ATTR_BATTERY_LEVEL, STATE_OFF, STATE_ON
from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=120)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Tahoma controller devices."""
_LOGGER.debug("Setup Tahoma Binary sensor platform")
controller = hass.data[TAHOMA_DOMAIN]["controller"]
devices = []
for device in hass.data[TAHOMA_DOMAIN]["devices"]["smoke"]:
devices.append(TahomaBinarySensor(device, controller))
add_entities(devices, True)
class TahomaBinarySensor(TahomaDevice, BinarySensorDevice):
"""Representation of a Tahoma Binary Sensor."""
def __init__(self, tahoma_device, controller):
"""Initialize the sensor."""
super().__init__(tahoma_device, controller)
self._state = None
self._icon = None
self._battery = None
self._available = False
@property
def is_on(self):
"""Return the state of the sensor."""
return bool(self._state == STATE_ON)
@property
def device_class(self):
"""Return the class of the device."""
if self.tahoma_device.type == "rtds:RTDSSmokeSensor":
return "smoke"
return None
@property
def icon(self):
"""Icon for device by its type."""
return self._icon
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attr = {}
super_attr = super().device_state_attributes
if super_attr is not None:
attr.update(super_attr)
if self._battery is not None:
attr[ATTR_BATTERY_LEVEL] = self._battery
return attr
@property
def available(self):
"""Return True if entity is available."""
return self._available
def update(self):
"""Update the state."""
self.controller.get_states([self.tahoma_device])
if self.tahoma_device.type == "rtds:RTDSSmokeSensor":
if self.tahoma_device.active_states["core:SmokeState"] == "notDetected":
self._state = STATE_OFF
else:
self._state = STATE_ON
if "core:SensorDefectState" in self.tahoma_device.active_states:
# 'lowBattery' for low battery warning. 'dead' for not available.
self._battery = self.tahoma_device.active_states["core:SensorDefectState"]
self._available = bool(self._battery != "dead")
else:
self._battery = None
self._available = True
if self._state == STATE_ON:
self._icon = "mdi:fire"
elif self._battery == "lowBattery":
self._icon = "mdi:battery-alert"
else:
self._icon = None
_LOGGER.debug("Update %s, state: %s", self._name, self._state)

View File

@ -1,249 +0,0 @@
"""Support for Tahoma cover - shutters etc."""
from datetime import timedelta
import logging
from homeassistant.components.cover import (
ATTR_POSITION,
DEVICE_CLASS_AWNING,
DEVICE_CLASS_BLIND,
DEVICE_CLASS_CURTAIN,
DEVICE_CLASS_GARAGE,
DEVICE_CLASS_SHUTTER,
DEVICE_CLASS_WINDOW,
CoverDevice,
)
from homeassistant.util.dt import utcnow
from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
_LOGGER = logging.getLogger(__name__)
ATTR_MEM_POS = "memorized_position"
ATTR_RSSI_LEVEL = "rssi_level"
ATTR_LOCK_START_TS = "lock_start_ts"
ATTR_LOCK_END_TS = "lock_end_ts"
ATTR_LOCK_LEVEL = "lock_level"
ATTR_LOCK_ORIG = "lock_originator"
HORIZONTAL_AWNING = "io:HorizontalAwningIOComponent"
TAHOMA_DEVICE_CLASSES = {
"io:ExteriorVenetianBlindIOComponent": DEVICE_CLASS_BLIND,
HORIZONTAL_AWNING: DEVICE_CLASS_AWNING,
"io:RollerShutterGenericIOComponent": DEVICE_CLASS_SHUTTER,
"io:RollerShutterUnoIOComponent": DEVICE_CLASS_SHUTTER,
"io:RollerShutterVeluxIOComponent": DEVICE_CLASS_SHUTTER,
"io:RollerShutterWithLowSpeedManagementIOComponent": DEVICE_CLASS_SHUTTER,
"io:VerticalExteriorAwningIOComponent": DEVICE_CLASS_AWNING,
"io:VerticalInteriorBlindVeluxIOComponent": DEVICE_CLASS_BLIND,
"io:WindowOpenerVeluxIOComponent": DEVICE_CLASS_WINDOW,
"io:GarageOpenerIOComponent": DEVICE_CLASS_GARAGE,
"io:DiscreteGarageOpenerIOComponent": DEVICE_CLASS_GARAGE,
"rts:BlindRTSComponent": DEVICE_CLASS_BLIND,
"rts:CurtainRTSComponent": DEVICE_CLASS_CURTAIN,
"rts:DualCurtainRTSComponent": DEVICE_CLASS_CURTAIN,
"rts:ExteriorVenetianBlindRTSComponent": DEVICE_CLASS_BLIND,
"rts:RollerShutterRTSComponent": DEVICE_CLASS_SHUTTER,
"rts:VenetianBlindRTSComponent": DEVICE_CLASS_BLIND,
}
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Tahoma covers."""
controller = hass.data[TAHOMA_DOMAIN]["controller"]
devices = []
for device in hass.data[TAHOMA_DOMAIN]["devices"]["cover"]:
devices.append(TahomaCover(device, controller))
add_entities(devices, True)
class TahomaCover(TahomaDevice, CoverDevice):
"""Representation a Tahoma Cover."""
def __init__(self, tahoma_device, controller):
"""Initialize the device."""
super().__init__(tahoma_device, controller)
self._closure = 0
# 100 equals open
self._position = 100
self._closed = False
self._rssi_level = None
self._icon = None
# Can be 0 and bigger
self._lock_timer = 0
self._lock_start_ts = None
self._lock_end_ts = None
# Can be 'comfortLevel1', 'comfortLevel2', 'comfortLevel3',
# 'comfortLevel4', 'environmentProtection', 'humanProtection',
# 'userLevel1', 'userLevel2'
self._lock_level = None
# Can be 'LSC', 'SAAC', 'SFC', 'UPS', 'externalGateway', 'localUser',
# 'myself', 'rain', 'security', 'temperature', 'timer', 'user', 'wind'
self._lock_originator = None
def update(self):
"""Update method."""
self.controller.get_states([self.tahoma_device])
# For vertical covers
self._closure = self.tahoma_device.active_states.get("core:ClosureState")
# For horizontal covers
if self._closure is None:
self._closure = self.tahoma_device.active_states.get("core:DeploymentState")
# For all, if available
if "core:PriorityLockTimerState" in self.tahoma_device.active_states:
old_lock_timer = self._lock_timer
self._lock_timer = self.tahoma_device.active_states[
"core:PriorityLockTimerState"
]
# Derive timestamps from _lock_timer, only if not already set or
# something has changed
if self._lock_timer > 0:
_LOGGER.debug("Update %s, lock_timer: %d", self._name, self._lock_timer)
if self._lock_start_ts is None:
self._lock_start_ts = utcnow()
if self._lock_end_ts is None or old_lock_timer != self._lock_timer:
self._lock_end_ts = utcnow() + timedelta(seconds=self._lock_timer)
else:
self._lock_start_ts = None
self._lock_end_ts = None
else:
self._lock_timer = 0
self._lock_start_ts = None
self._lock_end_ts = None
self._lock_level = self.tahoma_device.active_states.get(
"io:PriorityLockLevelState"
)
self._lock_originator = self.tahoma_device.active_states.get(
"io:PriorityLockOriginatorState"
)
self._rssi_level = self.tahoma_device.active_states.get("core:RSSILevelState")
# Define which icon to use
if self._lock_timer > 0:
if self._lock_originator == "wind":
self._icon = "mdi:weather-windy"
else:
self._icon = "mdi:lock-alert"
else:
self._icon = None
# Define current position.
# _position: 0 is closed, 100 is fully open.
# 'core:ClosureState': 100 is closed, 0 is fully open.
if self._closure is not None:
if self.tahoma_device.type == HORIZONTAL_AWNING:
self._position = self._closure
else:
self._position = 100 - self._closure
if self._position <= 5:
self._position = 0
if self._position >= 95:
self._position = 100
self._closed = self._position == 0
else:
self._position = None
if "core:OpenClosedState" in self.tahoma_device.active_states:
self._closed = (
self.tahoma_device.active_states["core:OpenClosedState"] == "closed"
)
else:
self._closed = False
_LOGGER.debug("Update %s, position: %d", self._name, self._position)
@property
def current_cover_position(self):
"""Return current position of cover."""
return self._position
def set_cover_position(self, **kwargs):
"""Move the cover to a specific position."""
if self.tahoma_device.type == "io:WindowOpenerVeluxIOComponent":
command = "setClosure"
else:
command = "setPosition"
if self.tahoma_device.type == HORIZONTAL_AWNING:
self.apply_action(command, kwargs.get(ATTR_POSITION, 0))
else:
self.apply_action(command, 100 - kwargs.get(ATTR_POSITION, 0))
@property
def is_closed(self):
"""Return if the cover is closed."""
return self._closed
@property
def device_class(self):
"""Return the class of the device."""
return TAHOMA_DEVICE_CLASSES.get(self.tahoma_device.type)
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attr = {}
super_attr = super().device_state_attributes
if super_attr is not None:
attr.update(super_attr)
if "core:Memorized1PositionState" in self.tahoma_device.active_states:
attr[ATTR_MEM_POS] = self.tahoma_device.active_states[
"core:Memorized1PositionState"
]
if self._rssi_level is not None:
attr[ATTR_RSSI_LEVEL] = self._rssi_level
if self._lock_start_ts is not None:
attr[ATTR_LOCK_START_TS] = self._lock_start_ts.isoformat()
if self._lock_end_ts is not None:
attr[ATTR_LOCK_END_TS] = self._lock_end_ts.isoformat()
if self._lock_level is not None:
attr[ATTR_LOCK_LEVEL] = self._lock_level
if self._lock_originator is not None:
attr[ATTR_LOCK_ORIG] = self._lock_originator
return attr
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
return self._icon
def open_cover(self, **kwargs):
"""Open the cover."""
self.apply_action("open")
def close_cover(self, **kwargs):
"""Close the cover."""
self.apply_action("close")
def stop_cover(self, **kwargs):
"""Stop the cover."""
if (
self.tahoma_device.type
== "io:RollerShutterWithLowSpeedManagementIOComponent"
):
self.apply_action("setPosition", "secured")
elif self.tahoma_device.type in (
"rts:BlindRTSComponent",
"io:ExteriorVenetianBlindIOComponent",
"rts:VenetianBlindRTSComponent",
"rts:DualCurtainRTSComponent",
"rts:ExteriorVenetianBlindRTSComponent",
"rts:BlindRTSComponent",
):
self.apply_action("my")
elif self.tahoma_device.type in (
HORIZONTAL_AWNING,
"io:RollerShutterGenericIOComponent",
"io:VerticalExteriorAwningIOComponent",
"io:VerticalInteriorBlindVeluxIOComponent",
"io:WindowOpenerVeluxIOComponent",
):
self.apply_action("stop")
else:
self.apply_action("stopIdentify")

View File

@ -1,12 +0,0 @@
{
"domain": "tahoma",
"name": "Tahoma",
"documentation": "https://www.home-assistant.io/integrations/tahoma",
"requirements": [
"tahoma-api==0.0.14"
],
"dependencies": [],
"codeowners": [
"@philklei"
]
}

View File

@ -1,41 +0,0 @@
"""Support for Tahoma scenes."""
import logging
from homeassistant.components.scene import Scene
from . import DOMAIN as TAHOMA_DOMAIN
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the Tahoma scenes."""
controller = hass.data[TAHOMA_DOMAIN]["controller"]
scenes = []
for scene in hass.data[TAHOMA_DOMAIN]["scenes"]:
scenes.append(TahomaScene(scene, controller))
add_entities(scenes, True)
class TahomaScene(Scene):
"""Representation of a Tahoma scene entity."""
def __init__(self, tahoma_scene, controller):
"""Initialize the scene."""
self.tahoma_scene = tahoma_scene
self.controller = controller
self._name = self.tahoma_scene.name
def activate(self):
"""Activate the scene."""
self.controller.launch_action_group(self.tahoma_scene.oid)
@property
def name(self):
"""Return the name of the scene."""
return self._name
@property
def device_state_attributes(self):
"""Return the state attributes of the scene."""
return {"tahoma_scene_oid": self.tahoma_scene.oid}

View File

@ -1,106 +0,0 @@
"""Support for Tahoma sensors."""
from datetime import timedelta
import logging
from homeassistant.const import ATTR_BATTERY_LEVEL
from homeassistant.helpers.entity import Entity
from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=60)
ATTR_RSSI_LEVEL = "rssi_level"
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Tahoma controller devices."""
controller = hass.data[TAHOMA_DOMAIN]["controller"]
devices = []
for device in hass.data[TAHOMA_DOMAIN]["devices"]["sensor"]:
devices.append(TahomaSensor(device, controller))
add_entities(devices, True)
class TahomaSensor(TahomaDevice, Entity):
"""Representation of a Tahoma Sensor."""
def __init__(self, tahoma_device, controller):
"""Initialize the sensor."""
self.current_value = None
self._available = False
super().__init__(tahoma_device, controller)
@property
def state(self):
"""Return the name of the sensor."""
return self.current_value
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
if self.tahoma_device.type == "Temperature Sensor":
return None
if self.tahoma_device.type == "io:SomfyContactIOSystemSensor":
return None
if self.tahoma_device.type == "io:SomfyBasicContactIOSystemSensor":
return None
if self.tahoma_device.type == "io:LightIOSystemSensor":
return "lx"
if self.tahoma_device.type == "Humidity Sensor":
return "%"
if self.tahoma_device.type == "rtds:RTDSContactSensor":
return None
if self.tahoma_device.type == "rtds:RTDSMotionSensor":
return None
def update(self):
"""Update the state."""
self.controller.get_states([self.tahoma_device])
if self.tahoma_device.type == "io:LightIOSystemSensor":
self.current_value = self.tahoma_device.active_states["core:LuminanceState"]
self._available = bool(
self.tahoma_device.active_states.get("core:StatusState") == "available"
)
if self.tahoma_device.type == "io:SomfyContactIOSystemSensor":
self.current_value = self.tahoma_device.active_states["core:ContactState"]
self._available = bool(
self.tahoma_device.active_states.get("core:StatusState") == "available"
)
if self.tahoma_device.type == "io:SomfyBasicContactIOSystemSensor":
self.current_value = self.tahoma_device.active_states["core:ContactState"]
self._available = bool(
self.tahoma_device.active_states.get("core:StatusState") == "available"
)
if self.tahoma_device.type == "rtds:RTDSContactSensor":
self.current_value = self.tahoma_device.active_states["core:ContactState"]
self._available = True
if self.tahoma_device.type == "rtds:RTDSMotionSensor":
self.current_value = self.tahoma_device.active_states["core:OccupancyState"]
self._available = True
_LOGGER.debug("Update %s, value: %d", self._name, self.current_value)
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attr = {}
super_attr = super().device_state_attributes
if super_attr is not None:
attr.update(super_attr)
if "core:RSSILevelState" in self.tahoma_device.active_states:
attr[ATTR_RSSI_LEVEL] = self.tahoma_device.active_states[
"core:RSSILevelState"
]
if "core:SensorDefectState" in self.tahoma_device.active_states:
attr[ATTR_BATTERY_LEVEL] = self.tahoma_device.active_states[
"core:SensorDefectState"
]
return attr
@property
def available(self):
"""Return True if entity is available."""
return self._available

View File

@ -1,109 +0,0 @@
"""Support for Tahoma switches."""
import logging
from homeassistant.components.switch import SwitchDevice
from homeassistant.const import STATE_OFF, STATE_ON
from . import DOMAIN as TAHOMA_DOMAIN, TahomaDevice
_LOGGER = logging.getLogger(__name__)
ATTR_RSSI_LEVEL = "rssi_level"
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up Tahoma switches."""
controller = hass.data[TAHOMA_DOMAIN]["controller"]
devices = []
for switch in hass.data[TAHOMA_DOMAIN]["devices"]["switch"]:
devices.append(TahomaSwitch(switch, controller))
add_entities(devices, True)
class TahomaSwitch(TahomaDevice, SwitchDevice):
"""Representation a Tahoma Switch."""
def __init__(self, tahoma_device, controller):
"""Initialize the switch."""
super().__init__(tahoma_device, controller)
self._state = STATE_OFF
self._skip_update = False
self._available = False
def update(self):
"""Update method."""
# Postpone the immediate state check for changes that take time.
if self._skip_update:
self._skip_update = False
return
self.controller.get_states([self.tahoma_device])
if self.tahoma_device.type == "io:OnOffLightIOComponent":
if self.tahoma_device.active_states.get("core:OnOffState") == "on":
self._state = STATE_ON
else:
self._state = STATE_OFF
self._available = bool(
self.tahoma_device.active_states.get("core:StatusState") == "available"
)
_LOGGER.debug("Update %s, state: %s", self._name, self._state)
@property
def device_class(self):
"""Return the class of the device."""
if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
return "garage"
return None
def turn_on(self, **kwargs):
"""Send the on command."""
_LOGGER.debug("Turn on: %s", self._name)
if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
self.toggle()
else:
self.apply_action("on")
self._skip_update = True
self._state = STATE_ON
def turn_off(self, **kwargs):
"""Send the off command."""
_LOGGER.debug("Turn off: %s", self._name)
if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
return
self.apply_action("off")
self._skip_update = True
self._state = STATE_OFF
def toggle(self, **kwargs):
"""Click the switch."""
self.apply_action("cycle")
@property
def is_on(self):
"""Get whether the switch is in on state."""
if self.tahoma_device.type == "rts:GarageDoor4TRTSComponent":
return False
return bool(self._state == STATE_ON)
@property
def device_state_attributes(self):
"""Return the device state attributes."""
attr = {}
super_attr = super().device_state_attributes
if super_attr is not None:
attr.update(super_attr)
if "core:RSSILevelState" in self.tahoma_device.active_states:
attr[ATTR_RSSI_LEVEL] = self.tahoma_device.active_states[
"core:RSSILevelState"
]
return attr
@property
def available(self):
"""Return True if entity is available."""
return self._available

View File

@ -1906,9 +1906,6 @@ swisshydrodata==0.0.3
# homeassistant.components.synology_srm
synology-srm==0.0.7
# homeassistant.components.tahoma
tahoma-api==0.0.14
# homeassistant.components.tank_utility
tank_utility==1.4.0