Create new cover component(not working)

Firstly, just want to thank everyone who’s worked on the development of Home Assistant and the community for all their input and making it so simple and easy to use.

I want to create my own component for other people to use, my small way of giving back however, ive been using Home Assistant for the past 6 months and trying to teach myself python in the process (no programming background).

Im currently using Home Assistant as a setup for all my components and then have HADashboard setup through out my house to control all my devices. The blinds i wish to control are controlled via http request which i currently have setup using the rest_command and then i use scripts to call each of those commands and in HADashboard i have up, down and stop buttons which run those scripts. The http requests are all basically the same the only variant is the controllers IP address and the blind ID code such as this

http://<IP_ADDRESS>/neo/v1/transmit?command=<BLIND_CODE>-up&id=<CONTROLLER_ID>

What i would like to do is setup the component so that in the Home Assistant config.yaml you just enter the required fields ie ip address, blind code, controller id.

Ive read the Home Assistant docs but im struggling to understand the steps i need to take. Do i need to create a new entity class to connect to the blinds controller? Where would i find documentation how to write such a script and then create the component? I feel like this is a pretty straight forward component to make… is there a template anyone can think of i could use to base this off?

Sorry for basically asking how to do it all from scratch, im trying to teach myself to code outside of my full time job. I’ve written a few simple python scripts but thats it really. Appreciate any help.

Okay so I think I’ve made some progress looking at existing cover components and trying to adapt upon those. At first I was getting errors saying "couldn’t find component neocontroller.py even though it was in the custom_components folder and other custom components were working, then I realised I wasn’t importing standard homeassistant.const ‘configs’. Once I fixed that up home assistant finally restart with no errors but there is no new cover showing.

I get absolutely no errors in the log about the custom component so im confused as to whats going on, can any one help me. Here is the custom component below. Again, I have no programming background just trying to teach my self by looking at existing components;

"""
Support for Neo Controller.
For more details about this platform, please refer to the documentation
https://home-assistant.io/components/cover.neocontroller/
"""
import logging
import requests

import voluptuous as vol

from homeassistant.components.cover import CoverDevice
from homeassistant.const import (CONF_IP_ADDRESS, CONF_ID, CONF_CODE)
import homeassistant.helpers.config_validation as cv

DEFAULT_NAME = 'neocontroller'

COVER_SCHEMA = vol.Schema({
    vol.Required(CONF_IP_ADDRESS): cv.string,
    vol.Required(CONF_ID): cv.string,
    vol.Required(CONF_CODE): cv.string
})

DEFAULT_PORT = '8838'

blindDown = ("http://{}:{}/neo/v1/transmit?command={}-dn&id={}".format(CONF_IP_ADDRESS, DEFAULT_PORT, CONF_CODE, CONF_ID))
blindUp = ("http://{}:{}/neo/v1/transmit?command={}-up&id={}".format(CONF_IP_ADDRESS, DEFAULT_PORT, CONF_CODE, CONF_ID))
blindStop = ("http://{}:{}/neo/v1/transmit?command={}-sp&id={}".format(CONF_IP_ADDRESS, DEFAULT_PORT, CONF_CODE, CONF_ID))


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up Neo Controller covers."""

    ip_address = config.get(CONF_IP_ADDRESS)
    id = config.get(CONF_ID)
    code = config.get(CONF_CODE)


class neocontroller(CoverDevice):
    """Representation of Neo Controller"""

    @property
    def close_cover(self):
        """Close the cover."""
        requests.get(blindDown)

    def open_cover(self):
        """Open the cover."""
        requests.get(blindUp)

    def stop_cover(self):
        """Stop the cover."""
        requests.get(blindStop)

Can anyone help me with whats missing? My config looks like this;

  - platform: neocontroller
    ip_address: 10.0.0.37
    id: <controller id>
    code: 116.005-01

So I managed to get it showing in the HASSIO front end but nothing is actually working. Is there anyone out there that can help me.

My custom component is looking like this now. I’ve used another cover component as my template and altered that. If I hard code the http get request in the close, open and stop functions it works but I am trying to get the information entered in from the config.yaml file to populate the get requests.

import logging
import requests

import voluptuous as vol

from homeassistant.components.cover import (
    CoverDevice, PLATFORM_SCHEMA, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP)
from homeassistant.const import (
    CONF_IP_ADDRESS, CONF_ID, CONF_CODE, CONF_NAME, CONF_COVERS, CONF_DEVICE, STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

DEFAULT_NAME = 'neocontroller'

CONF_DEVICE_ID = 'device_id'

STATE_CLOSING = 'closing'
STATE_OFFLINE = 'offline'
STATE_OPENING = 'opening'
STATE_STOPPED = 'stopped'

COVER_SCHEMA = vol.Schema({
    vol.Required(CONF_IP_ADDRESS): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Required(CONF_ID): cv.string,
    vol.Required(CONF_CODE): cv.string
})

blindDown = 'http://{}:8838/neo/v1/transmit?command={}-dn&id={}'.format(CONF_IP_ADDRESS, CONF_CODE, CONF_ID)
blindUp = 'http://{}:8838/neo/v1/transmit?command={}-up&id={}'.format(CONF_IP_ADDRESS, CONF_CODE, CONF_ID)
blindStop = 'http://{}:8838/neo/v1/transmit?command={}-sp&id={}'.format(CONF_IP_ADDRESS, CONF_CODE, CONF_ID)

############

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the neocontroller covers."""
    covers = []
    devices = config.get(CONF_COVERS)

    for device_id, device_config in devices.items():
        args = {
            CONF_IP_ADDRESS: device_config.get(CONF_IP_ADDRESS),
            CONF_ID: device_config.get(CONF_ID),
            CONF_CODE: device_config.get(CONF_CODE),
            CONF_NAME: device_config.get(CONF_NAME),
            CONF_DEVICE_ID: device_config.get(CONF_DEVICE, device_id),
        }

        covers.append(neocontroller(hass, args))

    add_devices(covers, True)


class neocontroller(CoverDevice):
    """Representation of NeoController cover."""

    # pylint: disable=no-self-use
    def __init__(self, hass, args):
        """Initialize the cover."""
        self.hass = hass
        self._name = args[CONF_NAME]
        self.device_id = args['device_id']
        self._available = True
        self._state = None

    @property
    def name(self):
        """Return the name of the cover."""
        return self._name

    @property
    def available(self):
        """Return True if entity is available."""
        return self._available
        
################

    @property
    def is_closed(self):
        """Return if the cover is closed."""
        if self._state in [STATE_UNKNOWN, STATE_OFFLINE]:
            return None
        return self._state in [STATE_CLOSED, STATE_OPENING]

    @property
    def close_cover(self):
        """Close the cover."""
        requests.get(blindDown)

    def open_cover(self):
        """Open the cover."""
        requests.get(blindUp)

    def stop_cover(self):
        """Stop the cover."""
        requests.get(blindStop)
        
    @property
    def device_class(self):
        """Return the class of this device, from component DEVICE_CLASSES."""
        return 'garage'

    @property
    def supported_features(self):
        """Flag supported features."""
        return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP

config file looks like this

  - platform: neocontroller
    covers:
      blind:
        ip_address: 10.0.0.37
        id: <controller id>
        code: 116.005-01

Try this on for size:

import logging
import requests

import voluptuous as vol

from homeassistant.components.cover import (
    CoverDevice, PLATFORM_SCHEMA, SUPPORT_OPEN, SUPPORT_CLOSE, SUPPORT_STOP)
from homeassistant.const import (
    CONF_IP_ADDRESS, CONF_ID, CONF_CODE, CONF_NAME, CONF_COVERS, CONF_DEVICE, STATE_CLOSED, STATE_OPEN, STATE_UNKNOWN)
import homeassistant.helpers.config_validation as cv

_LOGGER = logging.getLogger(__name__)

DEFAULT_NAME = 'neocontroller'

CONF_DEVICE_ID = 'device_id'

STATE_CLOSING = 'closing'
STATE_OFFLINE = 'offline'
STATE_OPENING = 'opening'
STATE_STOPPED = 'stopped'

COVER_SCHEMA = vol.Schema({
    vol.Required(CONF_IP_ADDRESS): cv.string,
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Required(CONF_ID): cv.string,
    vol.Required(CONF_CODE): cv.string
})

blindDown = 'http://{}:8838/neo/v1/transmit?command={}-dn&id={}'
blindUp = 'http://{}:8838/neo/v1/transmit?command={}-up&id={}'
blindStop = 'http://{}:8838/neo/v1/transmit?command={}-sp&id={}'

############

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_COVERS): vol.Schema({cv.slug: COVER_SCHEMA}),
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the neocontroller covers."""
    covers = []
    devices = config.get(CONF_COVERS)

    for device_id, device_config in devices.items():
        args = {
            CONF_IP_ADDRESS: device_config.get(CONF_IP_ADDRESS),
            CONF_ID: device_config.get(CONF_ID),
            CONF_CODE: device_config.get(CONF_CODE),
            CONF_NAME: device_config.get(CONF_NAME),
            CONF_DEVICE_ID: device_config.get(CONF_DEVICE, device_id),
        }

        covers.append(neocontroller(hass, args))

    add_devices(covers, True)


class neocontroller(CoverDevice):
    """Representation of NeoController cover."""

    # pylint: disable=no-self-use
    def __init__(self, hass, args):
        """Initialize the cover."""
        self.hass = hass
        self._name = args[CONF_NAME]
        self.device_id = args['device_id']
        self._ip_addr = args[CONF_IP_ADDRESS]
        self._id      = args[CONF_ID]
        self._code    = args[CONF_CODE]
        self._available = True
        self._state = None

    @property
    def name(self):
        """Return the name of the cover."""
        return self._name

    @property
    def available(self):
        """Return True if entity is available."""
        return self._available
        
################

    @property
    def is_closed(self):
        """Return if the cover is closed."""
        if self._state in [STATE_UNKNOWN, STATE_OFFLINE]:
            return None
        return self._state in [STATE_CLOSED, STATE_OPENING]

    @property
    def close_cover(self):
        """Close the cover."""
        requests.get(blindDown.format(self._ip_addr, self._code, self._id))

    def open_cover(self):
        """Open the cover."""
        requests.get(blindUp.format(self._ip_addr, self._code, self._id))

    def stop_cover(self):
        """Stop the cover."""
        requests.get(blindStop.format(self._ip_addr, self._code, self._id))
        
    @property
    def device_class(self):
        """Return the class of this device, from component DEVICE_CLASSES."""
        return 'garage'

    @property
    def supported_features(self):
        """Flag supported features."""
        return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP
1 Like

You sir are my hero. Thank you so much, that did the trick.

I don’t have a programming background so was trying to piece this all together based on another component and have been scratching my head for a couple weeks trying to find what’s missing.

I feel like the code is pretty messy and possibly contains a few unnecessary elements so my next step will be to clean it.

1 Like

Glad to help. Let me know if you have any questions about what I did.

Would you mind marking this topic solved by clicking on one of the “Select if this reply solves the problem” check boxes? That way others, when skimming through the list of topics, will know that you don’t need help anymore.

I straight away saw what you did, always such a simple solution but makes so much sense. Hopefully based on that i can clean up the code a bit now.

Thank you again… and i have marked as a solution.

1 Like

Hi Adam,

I am new to HA but have been messing with Vera for a couple of years and have it fairly well integrated. I have a whole house full of Neo Blinds and am controlling them via http calls (in Vera). Would love to know if your integration is ready to use? Cheers Markus