Taking a crack at UPB platform development

Thanks, I’ll give this a try. But I’m running Hassio, so I won’t be able to install the upb-cli. I guess I’ll switch to Hassbian, then I’ll be able to install the upb-cli ?

Ahh yes. That’s a good point. I’m not sure how that’ll work. I just run on straight linux.

I’m guessing you can modify the docker container somehow but I’m not sure.

Also, here is how to make a hassio addon but it doesn’t look easy at first glance.

Are you running in a Python Virtual Environment?

Yes

Thanks! I’m VERY new to all of this but it looks like Hassbian might work then…

I’ll work on it this weekend!

@eric24 @fabaff …and anyone else?
Maybe one of you can help. There is what I have so far: https://pastebin.com/czWwKbhZ

I’m trying to add to my platform where it listens on the serial port for updates and then applies them to the entity’s state. I’m stuck at line 180. I get the data from the serial port, decode it into a light/brightness level. But I can’t figure out how to tell HASS about the new values. If I do something like hass.states.async_set('light.upb_office_lamp', 'on'), then it gets over-written by (I think) the state machine.

Thanks.

Progress so far. I spun up a Hassbian install on a Pi 3b and migrated all my Hassio configurations over. I installed Nodejs and upb-cli and that seem to have gone okay. I setup my UPB PIM with a USB to Serial adapter and configured everything to ttyUSB0. I installed your upb.py lights in my custom_components folder and added my devices to configuration.yaml. Everything looks good, but nothing is working. I’m testing the upb-cli with command line commands and even thought it’s not throwing any errors, I still have not be able to get it to active or deactivate any of my upb devices. It’s a start, and I know I’m getting close!!!

I’m having to regroup. My installation of Node is having issues with finding the bindings for the serial ports. This is why I can’t get upb-cli to send commands. It can formulate the commands just fine.

I’m not savvy enough with Linux to figure out what’s going on. So I was going to take another route and slightly change the upb.py to get the command and use python-serial to send them. BUT, now I’m running into a problem with importing serial into upb.py

Going to have to dig more… :frowning:

Here is my modified version of your upb.py since I could not get the upb-cli serial to work. Still no additional progress, but I got it working with your one-way hack:

"""
Support for UPB lights.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/light.upb/

Improvements:
- ditch upb-cli and do everything natively
- listen for status changes in real time on the network
- optionally poll all the devices in the network every x minutes for their
  status
- instead of recreating the light config in configuration.yaml, read the config
  from the upstart export file

Changes:
- only use upb-cli to generate command codes
- send commands to serial port with send_serial
- hard code path to upb-cli with PATH var

"""
import serial
import logging
import time
from subprocess import check_output, CalledProcessError, STDOUT

import voluptuous as vol

from homeassistant.components.light import (
    ATTR_BRIGHTNESS, ATTR_BRIGHTNESS_PCT, SUPPORT_BRIGHTNESS, Light,
    PLATFORM_SCHEMA)

from homeassistant.const import (CONF_NAME, CONF_ID, CONF_DEVICES, CONF_BRIGHTNESS)

import homeassistant.helpers.config_validation as cv

# Home Assistant depends on 3rd party packages for API specific code.
REQUIREMENTS = ['pyserial==3.4']

serial_port = ''
upb_net = ''

_LOGGER = logging.getLogger(__name__)

#DOMAIN = 'UPB'

CONF_SERIAL_PORT = 'serial_port'
CONF_UPB_NET = 'upb_net'
PATH = '/opt/nodejs/bin/'


PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_DEVICES): vol.All(cv.ensure_list, [
        {
            vol.Required(CONF_ID): cv.string,
            vol.Required(CONF_NAME): cv.string,
            vol.Optional(CONF_BRIGHTNESS): cv.string,
        }
    ]),
    vol.Required(CONF_SERIAL_PORT): cv.string,
    vol.Required(CONF_UPB_NET): cv.string,

})


# CONFIG_SCHEMA = vol.Schema({
#     DOMAIN: vol.Schema({
#         vol.Optional(CONF_SERIAL_PORT, default=''): cv.string,
#     }),
# }, extra=vol.ALLOW_EXTRA)


def dump(obj):
    for attr in dir(obj):
        if hasattr( obj, attr ):
            print( "obj.%s = %s" % (attr, getattr(obj, attr)))

# def get_unit_status(code):
#     """Get on/off status for given unit"""
#     output = check_output('heyu onstate ' + code, shell=True)
#     return int(output.decode('utf-8')[0])    

def send_serial(command):
    port = serial.Serial(serial_port, 4800, timeout=1)
    port.write(str.encode(chr(20) + str(command) + chr(13)))
    time.sleep(0.1)
    port.close()
    return "serial command sent"
        
def upb_command(command):
    """Execute UPB command and check output"""
    #print("config: " + config[CONF_SERIAL_PORT])
    # print("serial: "+ serial_port)
    # print("upbnet: "+ upb_net)
    #return check_output(['/opt/nodejs/bin/upb-cli','-p',serial_port,'-n',upb_net] + command.split(' '), stderr=STDOUT)
    command_code = check_output([PATH + 'upb-cli','-n',upb_net] + command.split(' '), stderr=STDOUT)
    output = send_serial(command_code.decode('ascii'))
    #print(output)
    
def upb_version():
    return check_output([PATH + 'upb-cli','-V'], stderr=STDOUT)

def setup_platform(hass, config, add_devices, discovery_info=None):
    """Set up the UPB Light platform"""
    
    global serial_port, upb_net

    serial_port = config.get(CONF_SERIAL_PORT)
    upb_net = config.get(CONF_UPB_NET)
    
    try:
        upb_version()
    except CalledProcessError as err:
        _LOGGER.error(err.output)
        return False

    add_devices(UPBLight(light) for light in config[CONF_DEVICES])

class UPBLight(Light):
    """Representation of an UPB Light"""

    def __init__(self, light):
        """Initialize an UPB Light"""
        self._light = light
        self._name = light['name']
        self._id = light['id']
        self._brightness = None
        self._state = None

    @property
    def name(self):
        """Return the display name of this light"""
        return self._name

    @property
    def supported_features(self):
        """Flag supported features"""
        return SUPPORT_BRIGHTNESS

    @property
    def brightness(self):
        """Return the brightness of the light"""
        return self._brightness

    @property
    def is_on(self):
        """Return true if light is on"""
        return self._state

    def turn_on(self, **kwargs):
        """Instruct the light to turn on"""   
        
        bright_pct = int((kwargs.get(ATTR_BRIGHTNESS,255)/255)*100)

        #print(str(self._id)) ## This is the id value from the configutaion.yaml
        #upb_command('-n 99 -i 83 -t device -c goto -l 100 --send -p /dev/ttyS1' )
        #upb_command(' -i ' + self._id + ' -t device -c goto -l ' + str(bright_pct) + ' --send' )
        upb_command(' -i ' + self._id + ' -t device -c goto -l ' + str(bright_pct))
        
        #print(kwargs)
        self._state = True

    def turn_off(self, **kwargs):
        """Instruct the light to turn off"""
        #upb_command(' -i ' + self._id + ' -t device -c goto -l 0 --send' )
        #time.sleep(1)
        #upb_command(' -i ' + self._id + ' -t device -c goto -l 0 --send' )
        upb_command(' -i ' + self._id + ' -t device -c deactivate 0' )
        self._state = False

##     def update(self):
##        """Fetch new state data for this light.
##        This is the only method that should fetch new data for Home Assistant.
##        """
##        self._state = bool(get_unit_status(self._id))
##        self._light.update()
##        self._state = self._light.is_on()
##        self._brightness = self._light.brightness
##        print("asdfupdate: " )        

PS: I also enabled the brightness parameter that was missing.

1 Like

If anyone cares, I got UPB to work bi-directional (for the most part). I couldn’t figure out how to do it properly so I have the upb component reading from temp files that a daemon updates as a separate process. It’s not a simple drop in solution like it should be. It requires some general understanding of linux so if anyone wants it I’ll PM you my nasty code.

I’m interested, however I won’t have much time to play this next week because my car is out of commission and I’m working on getting it fixed. The upper oil pan cracked where the oil pressure sensor goes.

Sent. Happy hacking :slight_smile:

Hi Brendon,

I was exploring the upb-cli app in github when I noticed that you mentioned to the author that you got it working with HA. Great.
I was considering about doing the same, but since I don’t have any idea of how to integrate it with Home Assistant, I was thinking about using MQTT for communicating with it. If you have an MQTT broker you can define MQTT lights and use the python paho library for receiving the commands from HA to upb-cli and returning status from upb-cli to HA. I currently use MQTT with some Sonoff switches and I’m super pleased with the quality of the status reporting (after manual changes at the switch). I’m thinking about scrapping my zigbee lights, becuase they don’t report status after turning them on/off locally.

That’s a great idea but I actually thought I was going to be able to make a native component after I got the hass->UPB working. My hack works well enough and seems to be only limited by features/bugs in upb-cli.

All this component really needs is some guidance from a real developer to get it “unhacked”, but I haven’t been able to find someone.

I’m interested in taking a look. I was pondering the idea of creating a daemon that would update my MQTT broker to track state… More hacking, I know, but I still have not been able to understand how the core code is doing this…

Hello,

Has there been any interest in UPB? I see a few things from a year ago, but is there anything more recent? I can see that my PCS PulseWorx UPB Powerline Interface Module - USB (PIM-U) is detected as “Simply Automated Inc. USB to Serial1” on hass.io’s console. But how do I get an integration or platform that works for it?

Thanks,
-Ambi

Nothing new. You can do one way easily with my custom component and two way with a bit of extra hackery.

OK, thanks! It looks like I’m moving to Z-Wave or Zigbee then!

Hi Brendon,
I am interested in taking a look and trying it. Having ~50 UPB switches makes it very desirable functionality to try.

Done. Sent