Help using config.get for custom platform / switch

Hi folks,

Apologies in advance for the ham-handed approach, but I’m a pretty miserable programmer. I’m integrating the Digital Loggers DIN III Relay in - actually, I have that working fine. It’s an eight port, internet addressable relay. Only I’ve found I have to hard code the settings. I don’t know enough object oriented programming to follow how the homeassistant.component.switch and voluptuous works. I program by example and pattern matching and it’s just failing me here.

I’ve used the AwsomeLights example and referenced the existing Netio and MyStrom custom switches as examples.

Here’s a section that happens to be working, but with the stuff I thought would work commented out.

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
# from homeassistant.const import (CONF_HOST, CONF_CONTROLLERNAME, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT, CONF_CYCLETIME)
from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['dlipower==0.7.165']

DEFAULT_CONTROLLERNAME = 'DINRelay'
DEFAULT_USERNAME = 'admin'
DEFAULT_PASSWORD = 'admin'
DEFAULT_TIMEOUT = 20
DEFAULT_CYCLETIME = 3

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    # vol.Optional(CONF_CONTROLLERNAME, default=DEFAULT_CONTROLLERNAME): cv.string,
    vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string,
    vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
    # vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT):
    #     vol.All(vol.Coerce(int), vol.Range(min=1, max=600)),
    # vol.Optional(CONF_CYCLETIME, default=DEFAULT_CYCLETIME):
    #     vol.All(vol.Coerce(int), vol.Range(min=1, max=600)),
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Find and return DIN III Relay switch."""
    import dlipower

    host = config.get(CONF_HOST)
    # controllername = config.get(CONF_CONTROLLERNAME)
    user = config.get(CONF_USERNAME)
    pswd = config.get(CONF_PASSWORD)
    # tout = config.get(CONF_TIMEOUT)
    # cycl = config.get(CONF_CYCLETIME)

    # host = '127.0.0.1'
    controllername = 'PoolRelay'
    # user = 'admin'
    # pswd = 'admin'
    tout = 20
    cycl = 3

Is there something special about host, username and password that that exist in the schema and the other things don’t? Or I’m looking at it assuming that the configuration smarts do some magic converting the yaml into things that will import of the form CONF_something. I don’t see why it would do that… other than that’s what every example I look at seems to have. Obviously, at the end I’m just accommodating the fact that it isn’t working and hard coding my stuff. My intended configuration.yaml would look like this:

switch:
  - platform: digitalloggers
    host: !secret pool_relay_address
    controllername: PoolRelays
    password: !secret pool_relay_password

I know I’m missing something pretty fundamental. I’m not new to python, though I’m not formally a programmer. I’ve not dealt with yaml before HA and I’ve never worked with voluptuous. Likewise, despite that I have the full eight ports of the relay showing up, updating and interacting with HA, I know there are still a few things to fix on it. It’s flooding the poor relay with requests for updates. I’ll need to borrow the throttling concept I’ve seen on some of the other custom switches and apply it here.

Please either tell me outright what I’m doing wrong or point me towards the resource to enlighten me. I’ve been searching all over the HA site and forum but I think I’m probably not searching for the right thing or looking for something so basic you folks just aren’t even talking about it. :slight_smile:

Thanks!

Dave

If I run your provided code slightly modified from <config_dir>/custom_components/sensor/digitalloggers.py

import logging

import voluptuous as vol

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['dlipower==0.7.165']

CONF_CYCLETIME = 'cycletime'

DEFAULT_NAME = 'DINRelay'
DEFAULT_USERNAME = 'admin'
DEFAULT_PASSWORD = 'admin'
DEFAULT_TIMEOUT = 20
DEFAULT_CYCLETIME = 3

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string,
    vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int,
    vol.Optional(CONF_CYCLETIME, default=DEFAULT_CYCLETIME): cv.positive_int,
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Find and return DIN III Relay switch."""
    import dlipower

    print(config)

With

switch:
  - platform: digitalloggers
    host: 192.168.2.4
    controllername: PoolRelays
    password: Secret

The config dict contains the configuration values.

{
    'username': 'admin',
    'password': 'Secret',
    'controllername': 'PoolRelays',
    'platform': 'digitalloggers',
    'host': '192.168.2.4',
    'name': 'DINRelay',
    'timeout': 20,
    'cycletime': 3
}

Hey, fabaff! Huge thanks.

Seeing that config makes it in there is more than I was able to do. To some extent I’m flying blind since I’m used to experimenting in Jupyter and can see/explore a bit. Having it run remotely on a RPi takes most of that away from me.

So… what still doesn’t make sense to me is the significance of this line:

from homeassistant.const import (CONF_HOST, CONF_NAME, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT)

For some reason, I’m trying to associate it with the configuration file in my head. When I just assume I prepend each value with CONF_ and stick it in there, I’d get an error with my CONF_CONTROLLERNAME, but not when I used CONF_NAME, as if there were allowed words and illegal ones. But I see you’re using some and then following up with:

CONF_CYCLETIME = 'cycletime'

Presumably, that’s taking care of the odd-ball config items that aren’t “constants”? If that’s the case, really none of mine are global constants and I probably shouldn’t be using .const at all since they’d be unique to each switch.

And, of course, all of this is just trying to follow the example put forward to use voluptuous. I can make use of the dict and reference things directly, but that’s not being a good citizen.

Here is what I have. It’s working well now, connecting to multiple relay switches and getting all of the items correctly from the config and using defaults where necessary. I still need to work in the netio-style throttling so it’ll stop hammering those poor switches. It seems to be checking status on three or four individual relays every second.

Comments welcome as I may be horribly misusing objects.

"""
Support for Digital Loggers DIN III Relays and possibly other items through Dwight Hubbard's, python-dlipower.

For more details about python-dlipower, please see https://github.com/dwighthubbard/python-dlipower
"""
import logging

import voluptuous as vol

from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_HOST, CONF_USERNAME, CONF_PASSWORD, CONF_TIMEOUT)
import homeassistant.helpers.config_validation as cv

REQUIREMENTS = ['dlipower==0.7.165']

CONF_CYCLETIME = 'cycletime'
CONF_CONTROLLERNAME = 'controllername'

DEFAULT_CONTROLLERNAME = 'DINRelay'
DEFAULT_USERNAME = 'admin'
DEFAULT_PASSWORD = 'admin'
DEFAULT_TIMEOUT = 20
DEFAULT_CYCLETIME = 3

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_HOST): cv.string,
    vol.Optional(CONF_CONTROLLERNAME, default=DEFAULT_CONTROLLERNAME): cv.string,
    vol.Optional(CONF_USERNAME, default=DEFAULT_USERNAME): cv.string,
    vol.Optional(CONF_PASSWORD, default=DEFAULT_PASSWORD): cv.string,
    vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT):
        vol.All(vol.Coerce(int), vol.Range(min=1, max=600)),
    vol.Optional(CONF_CYCLETIME, default=DEFAULT_CYCLETIME):
        vol.All(vol.Coerce(int), vol.Range(min=1, max=600)),

})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Find and return DIN III Relay switch."""
    import dlipower

    host = config.get(CONF_HOST)
    controllername = config.get(CONF_CONTROLLERNAME)
    user = config.get(CONF_USERNAME)
    pswd = config.get(CONF_PASSWORD)
    tout = config.get(CONF_TIMEOUT)
    cycl = config.get(CONF_CYCLETIME)

    power_switch = dlipower.PowerSwitch(hostname=host, userid=user, password=pswd, timeout=tout, cycletime=cycl)

    if not power_switch.verify():
        _LOGGER.error('Could not connect to DIN III Relay')
        return False

    for switch in power_switch:
        add_devices([DINRelay(controllername, host, switch.outlet_number, power_switch)])


class DINRelay(SwitchDevice):
    """Representation of a DIN III Relay switch."""

    def __init__(self, name, host, outletnumber, switchref):
        """Initialize the DIN III Relay switch."""
        import dlipower

        self._host = host
        self.controllername = name
        self.outletnumber = outletnumber
        self.switchref = switchref
        self.update()

    @property
    def name(self):
        """Return the display name of this light."""
        return self.controllername + '_' + self.switchref.get_outlet_name(outlet=self.outletnumber)

    @property
    def is_on(self):
        """Return true if light is on."""
        return self.switchref.status(outlet=self.outletnumber) == 'ON'

    def turn_on(self, **kwargs):
        """Instruct the light to turn on.        """
        self.switchref.on(outlet=self.outletnumber)

    def turn_off(self, **kwargs):
        """Instruct the light to turn off."""
        self.switchref.off(outlet=self.outletnumber)

    def update(self):
        """Fetch new state data for this light.

        This is the only method that should fetch new data for Home Assistant.
        """
        self.switchref.status(outlet=self.outletnumber)

We have a bunch of constants which are already defined. Check const.py for details.

Think of this as “custom” or platform-specific constant. The default values are assigned with voluptuous the rest with .get.

This should be CONF_NAME. We are using CONF_NAME to keep the configuration of different platforms aligned.

CONF_CONTROLLERNAME = 'controllername'

DEFAULT_CONTROLLERNAME = 'DINRelay'

Thanks, I understand it now. Seeing that those constants are just standardized labels explains a lot.

I was resistant to use NAME as what I was calling CONTROLLERNAME because I was keeping “name” as the individual relay/HA_switch and trying to distinguish the identity of the physical box that holds 8 relay as something else. But, different scopes, different locations… there is really no confusion to the prospective user at the point of configuration to say NAME.

I believe I’ve got the config side understood enough now to be dangerous. Thanks for all of your help!