Add support for the SCSGate device
Support the SCSGate device. This will allow home-assistant to interact with BTicino/Legrand MyHome system. Signed-off-by: Flavio Castelli <flavio@castelli.me>pull/1056/head
parent
3e35bc06fc
commit
8eef978241
|
@ -53,6 +53,9 @@ omit =
|
|||
homeassistant/components/rpi_gpio.py
|
||||
homeassistant/components/*/rpi_gpio.py
|
||||
|
||||
homeassistant/components/scsgate.py
|
||||
homeassistant/components/*/scsgate.py
|
||||
|
||||
homeassistant/components/binary_sensor/arest.py
|
||||
homeassistant/components/binary_sensor/rest.py
|
||||
homeassistant/components/browser.py
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
"""
|
||||
homeassistant.components.light.scsgate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for SCSGate lights.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.scsgate/
|
||||
"""
|
||||
import logging
|
||||
import homeassistant.components.scsgate as scsgate
|
||||
|
||||
from homeassistant.components.light import Light
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
|
||||
DEPENDENCIES = ['scsgate']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add the SCSGate swiches defined inside of the configuration file. """
|
||||
|
||||
devices = config.get('devices')
|
||||
lights = []
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if devices:
|
||||
for _, entity_info in devices.items():
|
||||
if entity_info['scs_id'] in scsgate.SCSGATE.devices:
|
||||
continue
|
||||
|
||||
logger.info("Adding %s scsgate.light", entity_info['name'])
|
||||
|
||||
name = entity_info['name']
|
||||
scs_id = entity_info['scs_id']
|
||||
light = SCSGateLight(
|
||||
name=name,
|
||||
scs_id=scs_id,
|
||||
logger=logger)
|
||||
lights.append(light)
|
||||
|
||||
add_devices_callback(lights)
|
||||
scsgate.SCSGATE.add_devices_to_register(lights)
|
||||
|
||||
|
||||
class SCSGateLight(Light):
|
||||
""" Provides a SCSGate light. """
|
||||
def __init__(self, scs_id, name, logger):
|
||||
self._name = name
|
||||
self._scs_id = scs_id
|
||||
self._toggled = False
|
||||
self._logger = logger
|
||||
|
||||
@property
|
||||
def scs_id(self):
|
||||
""" SCS ID """
|
||||
return self._scs_id
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed for a SCSGate light. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device if any. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
""" True if light is on. """
|
||||
return self._toggled
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
""" Turn the device on. """
|
||||
from scsgate.tasks import ToggleStatusTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
ToggleStatusTask(
|
||||
target=self._scs_id,
|
||||
toggled=True))
|
||||
|
||||
self._toggled = True
|
||||
self.update_ha_state()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
""" Turn the device off. """
|
||||
from scsgate.tasks import ToggleStatusTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
ToggleStatusTask(
|
||||
target=self._scs_id,
|
||||
toggled=False))
|
||||
|
||||
self._toggled = False
|
||||
self.update_ha_state()
|
||||
|
||||
def process_event(self, message):
|
||||
""" Handle a SCSGate message related with this light """
|
||||
if self._toggled == message.toggled:
|
||||
self._logger.info(
|
||||
"Light %s, ignoring message %s because state already active",
|
||||
self._scs_id, message)
|
||||
# Nothing changed, ignoring
|
||||
return
|
||||
|
||||
self._toggled = message.toggled
|
||||
self.update_ha_state()
|
||||
|
||||
command = "off"
|
||||
if self._toggled:
|
||||
command = "on"
|
||||
|
||||
self.hass.bus.fire(
|
||||
'button_pressed', {
|
||||
ATTR_ENTITY_ID: self._scs_id,
|
||||
'state': command
|
||||
}
|
||||
)
|
|
@ -0,0 +1,98 @@
|
|||
"""
|
||||
homeassistant.components.rollershutter.scsgate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Allows to configure a SCSGate rollershutter.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/rollershutter.scsgate/
|
||||
"""
|
||||
import logging
|
||||
import homeassistant.components.scsgate as scsgate
|
||||
from homeassistant.components.rollershutter import RollershutterDevice
|
||||
|
||||
|
||||
DEPENDENCIES = ['scsgate']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add the SCSGate swiches defined inside of the configuration file. """
|
||||
|
||||
devices = config.get('devices')
|
||||
rollershutters = []
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if devices:
|
||||
for _, entity_info in devices.items():
|
||||
if entity_info['scs_id'] in scsgate.SCSGATE.devices:
|
||||
continue
|
||||
|
||||
logger.info("Adding %s scsgate.rollershutter", entity_info['name'])
|
||||
|
||||
name = entity_info['name']
|
||||
scs_id = entity_info['scs_id']
|
||||
rollershutter = SCSGateRollerShutter(
|
||||
name=name,
|
||||
scs_id=scs_id,
|
||||
logger=logger)
|
||||
scsgate.SCSGATE.add_device(rollershutter)
|
||||
rollershutters.append(rollershutter)
|
||||
|
||||
add_devices_callback(rollershutters)
|
||||
|
||||
|
||||
# pylint: disable=too-many-arguments, too-many-instance-attributes
|
||||
class SCSGateRollerShutter(RollershutterDevice):
|
||||
""" Represents a rollershutter that can be controlled using SCSGate. """
|
||||
def __init__(self, scs_id, name, logger):
|
||||
self._scs_id = scs_id
|
||||
self._name = name
|
||||
self._logger = logger
|
||||
|
||||
@property
|
||||
def scs_id(self):
|
||||
""" SCSGate ID """
|
||||
return self._scs_id
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" The name of the rollershutter. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def current_position(self):
|
||||
"""
|
||||
Return current position of rollershutter.
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
return None
|
||||
|
||||
def move_up(self, **kwargs):
|
||||
""" Move the rollershutter up. """
|
||||
from scsgate.tasks import RaiseRollerShutterTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
RaiseRollerShutterTask(target=self._scs_id))
|
||||
|
||||
def move_down(self, **kwargs):
|
||||
""" Move the rollershutter down. """
|
||||
from scsgate.tasks import LowerRollerShutterTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
LowerRollerShutterTask(target=self._scs_id))
|
||||
|
||||
def stop(self, **kwargs):
|
||||
""" Stop the device. """
|
||||
from scsgate.tasks import HaltRollerShutterTask
|
||||
|
||||
scsgate.SCSGATE.append_task(HaltRollerShutterTask(target=self._scs_id))
|
||||
|
||||
def process_event(self, message):
|
||||
""" Handle a SCSGate message related with this rollershutter """
|
||||
self._logger.debug(
|
||||
"Rollershutter %s, got message %s",
|
||||
self._scs_id, message.toggled)
|
|
@ -0,0 +1,162 @@
|
|||
"""
|
||||
homeassistant.components.scsgate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Provides support for SCSGate components.
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/scsgate/
|
||||
"""
|
||||
import logging
|
||||
from threading import Lock
|
||||
from homeassistant.core import EVENT_HOMEASSISTANT_STOP
|
||||
|
||||
REQUIREMENTS = ['scsgate==0.1.0']
|
||||
|
||||
|
||||
DOMAIN = "scsgate"
|
||||
|
||||
SCSGATE = None
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SCSGate:
|
||||
""" Class dealing with the SCSGate device via scsgate.Reactor """
|
||||
|
||||
def __init__(self, device, logger):
|
||||
self._logger = logger
|
||||
self._devices = {}
|
||||
self._devices_to_register = {}
|
||||
self._devices_to_register_lock = Lock()
|
||||
self._device_being_registered = None
|
||||
self._device_being_registered_lock = Lock()
|
||||
|
||||
from scsgate.connection import Connection
|
||||
connection = Connection(device=device, logger=self._logger)
|
||||
|
||||
from scsgate.reactor import Reactor
|
||||
self._reactor = Reactor(
|
||||
connection=connection,
|
||||
logger=self._logger,
|
||||
handle_message=self.handle_message)
|
||||
|
||||
def handle_message(self, message):
|
||||
""" Method called whenever a message is seen on the bus """
|
||||
from scsgate.messages import StateMessage, ScenarioTriggeredMessage
|
||||
|
||||
self._logger.debug("Received message {}".format(message))
|
||||
if not isinstance(message, StateMessage) and \
|
||||
not isinstance(message, ScenarioTriggeredMessage):
|
||||
msg = "Ignored message {} - not releavant type".format(
|
||||
message)
|
||||
self._logger.debug(msg)
|
||||
return
|
||||
|
||||
if message.entity in self._devices:
|
||||
new_device_activated = False
|
||||
with self._devices_to_register_lock:
|
||||
if message.entity == self._device_being_registered:
|
||||
self._device_being_registered = None
|
||||
new_device_activated = True
|
||||
if new_device_activated:
|
||||
self._activate_next_device()
|
||||
|
||||
# pylint: disable=broad-except
|
||||
try:
|
||||
self._devices[message.entity].process_event(message)
|
||||
except Exception as exception:
|
||||
msg = "Exception while processing event: {}".format(
|
||||
exception)
|
||||
self._logger.error(msg)
|
||||
else:
|
||||
self._logger.info(
|
||||
"Ignoring state message for device {} because unknonw".format(
|
||||
message.entity))
|
||||
|
||||
@property
|
||||
def devices(self):
|
||||
""" Dictionary with known devices. Key is device ID,
|
||||
value is the device itself """
|
||||
return self._devices
|
||||
|
||||
def add_device(self, device):
|
||||
""" Adds the specified device to the list of the already registered ones.
|
||||
|
||||
Beware: this is not what you usually want to do, take
|
||||
a look at `add_devices_to_register`
|
||||
"""
|
||||
self._devices[device.scs_id] = device
|
||||
|
||||
def add_devices_to_register(self, devices):
|
||||
""" List of devices to be registered.
|
||||
|
||||
Arguments:
|
||||
* devices: list of devices to register
|
||||
"""
|
||||
with self._devices_to_register_lock:
|
||||
for device in devices:
|
||||
self._devices_to_register[device.scs_id] = device
|
||||
self._activate_next_device()
|
||||
|
||||
def _activate_next_device(self):
|
||||
""" Starts the activation of the 1st device inside of self._devices """
|
||||
from scsgate.tasks import GetStatusTask
|
||||
|
||||
with self._devices_to_register_lock:
|
||||
if len(self._devices_to_register) == 0:
|
||||
return
|
||||
_, device = self._devices_to_register.popitem()
|
||||
self._devices[device.scs_id] = device
|
||||
self._device_being_registered = device.scs_id
|
||||
self._reactor.append_task(GetStatusTask(target=device.scs_id))
|
||||
|
||||
def is_device_registered(self, device_id):
|
||||
""" Checks whether a device is already registered or not
|
||||
|
||||
Arguments:
|
||||
device_id: the ID of the device to look for
|
||||
"""
|
||||
with self._devices_to_register_lock:
|
||||
if device_id in self._devices_to_register.keys():
|
||||
return False
|
||||
|
||||
with self._device_being_registered_lock:
|
||||
if device_id == self._device_being_registered:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def start(self):
|
||||
""" Start the scsgate.Reactor """
|
||||
self._reactor.start()
|
||||
|
||||
def stop(self):
|
||||
""" Stop the scsgate.Reactor """
|
||||
self._reactor.stop()
|
||||
|
||||
def append_task(self, task):
|
||||
""" Registers a new task to be executed """
|
||||
self._reactor.append_task(task)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
""" Setup the SCSGate component. """
|
||||
device = config['scsgate']['device']
|
||||
global SCSGATE
|
||||
|
||||
# pylint: disable=broad-except
|
||||
try:
|
||||
SCSGATE = SCSGate(device=device, logger=_LOGGER)
|
||||
SCSGATE.start()
|
||||
except Exception as exception:
|
||||
_LOGGER.error("Cannot setup SCSGate component: %s", exception)
|
||||
return False
|
||||
|
||||
def stop_monitor(event):
|
||||
""" Invoked when home-assistant is exiting. Performs the necessary
|
||||
cleanups """
|
||||
_LOGGER.info("Stopping SCSGate monitor thread")
|
||||
SCSGATE.stop()
|
||||
|
||||
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_monitor)
|
||||
|
||||
return True
|
|
@ -0,0 +1,202 @@
|
|||
"""
|
||||
homeassistant.components.switch.scsgate
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Support for SCSGate switches.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.scsgate/
|
||||
"""
|
||||
import logging
|
||||
import homeassistant.components.scsgate as scsgate
|
||||
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
|
||||
|
||||
DEPENDENCIES = ['scsgate']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
|
||||
""" Add the SCSGate swiches defined inside of the configuration file. """
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_setup_traditional_switches(
|
||||
logger=logger,
|
||||
config=config,
|
||||
add_devices_callback=add_devices_callback)
|
||||
|
||||
_setup_scenario_switches(
|
||||
logger=logger,
|
||||
config=config,
|
||||
hass=hass)
|
||||
|
||||
|
||||
def _setup_traditional_switches(logger, config, add_devices_callback):
|
||||
""" Add traditional SCSGate switches """
|
||||
traditional = config.get('traditional')
|
||||
switches = []
|
||||
|
||||
if traditional:
|
||||
for _, entity_info in traditional.items():
|
||||
if entity_info['scs_id'] in scsgate.SCSGATE.devices:
|
||||
continue
|
||||
|
||||
logger.info(
|
||||
"Adding %s scsgate.traditional_switch", entity_info['name'])
|
||||
|
||||
name = entity_info['name']
|
||||
scs_id = entity_info['scs_id']
|
||||
|
||||
switch = SCSGateSwitch(
|
||||
name=name,
|
||||
scs_id=scs_id,
|
||||
logger=logger)
|
||||
switches.append(switch)
|
||||
|
||||
add_devices_callback(switches)
|
||||
scsgate.SCSGATE.add_devices_to_register(switches)
|
||||
|
||||
|
||||
def _setup_scenario_switches(logger, config, hass):
|
||||
""" Add only SCSGate scenario switches """
|
||||
scenario = config.get("scenario")
|
||||
|
||||
if scenario:
|
||||
for _, entity_info in scenario.items():
|
||||
if entity_info['scs_id'] in scsgate.SCSGATE.devices:
|
||||
continue
|
||||
|
||||
logger.info(
|
||||
"Adding %s scsgate.scenario_switch", entity_info['name'])
|
||||
|
||||
name = entity_info['name']
|
||||
scs_id = entity_info['scs_id']
|
||||
|
||||
switch = SCSGateScenarioSwitch(
|
||||
name=name,
|
||||
scs_id=scs_id,
|
||||
logger=logger,
|
||||
hass=hass)
|
||||
scsgate.SCSGATE.add_device(switch)
|
||||
|
||||
|
||||
class SCSGateSwitch(SwitchDevice):
|
||||
""" Provides a SCSGate switch. """
|
||||
def __init__(self, scs_id, name, logger):
|
||||
self._name = name
|
||||
self._scs_id = scs_id
|
||||
self._toggled = False
|
||||
self._logger = logger
|
||||
|
||||
@property
|
||||
def scs_id(self):
|
||||
""" SCS ID """
|
||||
return self._scs_id
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
""" No polling needed for a SCSGate switch. """
|
||||
return False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device if any. """
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
""" True if switch is on. """
|
||||
return self._toggled
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
""" Turn the device on. """
|
||||
from scsgate.tasks import ToggleStatusTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
ToggleStatusTask(
|
||||
target=self._scs_id,
|
||||
toggled=True))
|
||||
|
||||
self._toggled = True
|
||||
self.update_ha_state()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
""" Turn the device off. """
|
||||
from scsgate.tasks import ToggleStatusTask
|
||||
|
||||
scsgate.SCSGATE.append_task(
|
||||
ToggleStatusTask(
|
||||
target=self._scs_id,
|
||||
toggled=False))
|
||||
|
||||
self._toggled = False
|
||||
self.update_ha_state()
|
||||
|
||||
def process_event(self, message):
|
||||
""" Handle a SCSGate message related with this switch"""
|
||||
if self._toggled == message.toggled:
|
||||
self._logger.info(
|
||||
"Switch %s, ignoring message %s because state already active",
|
||||
self._scs_id, message)
|
||||
# Nothing changed, ignoring
|
||||
return
|
||||
|
||||
self._toggled = message.toggled
|
||||
self.update_ha_state()
|
||||
|
||||
command = "off"
|
||||
if self._toggled:
|
||||
command = "on"
|
||||
|
||||
self.hass.bus.fire(
|
||||
'button_pressed', {
|
||||
ATTR_ENTITY_ID: self._scs_id,
|
||||
'state': command
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SCSGateScenarioSwitch:
|
||||
""" Provides a SCSGate scenario switch.
|
||||
|
||||
This switch is always in a 'off" state, when toggled
|
||||
it's used to trigger events
|
||||
"""
|
||||
def __init__(self, scs_id, name, logger, hass):
|
||||
self._name = name
|
||||
self._scs_id = scs_id
|
||||
self._logger = logger
|
||||
self._hass = hass
|
||||
|
||||
@property
|
||||
def scs_id(self):
|
||||
""" SCS ID """
|
||||
return self._scs_id
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
""" Returns the name of the device if any. """
|
||||
return self._name
|
||||
|
||||
def process_event(self, message):
|
||||
""" Handle a SCSGate message related with this switch"""
|
||||
from scsgate.messages import StateMessage, ScenarioTriggeredMessage
|
||||
|
||||
if isinstance(message, StateMessage):
|
||||
scenario_id = message.bytes[4]
|
||||
elif isinstance(message, ScenarioTriggeredMessage):
|
||||
scenario_id = message.scenario
|
||||
else:
|
||||
self._logger.warn(
|
||||
"Scenario switch: received unknown message %s",
|
||||
message)
|
||||
return
|
||||
|
||||
self._hass.bus.fire(
|
||||
'scenario_switch_triggered', {
|
||||
ATTR_ENTITY_ID: int(self._scs_id),
|
||||
'scenario_id': int(scenario_id, 16)
|
||||
}
|
||||
)
|
|
@ -143,6 +143,9 @@ https://github.com/Danielhiversen/pyRFXtrx/archive/0.2.zip#RFXtrx==0.2
|
|||
# homeassistant.components.rpi_gpio
|
||||
# RPi.GPIO==0.6.1
|
||||
|
||||
# homeassistant.components.scsgate
|
||||
scsgate==0.1.0
|
||||
|
||||
# homeassistant.components.sensor.bitcoin
|
||||
blockchain==1.2.1
|
||||
|
||||
|
|
Loading…
Reference in New Issue