Expand the rpi_gpio component to support orangepi as well.#19732
Expand the rpi_gpio component to support orangepi as well.#19732mikalstill wants to merge 7 commits intohome-assistant:devfrom
Conversation
|
Hi @mikalstill, It seems you haven't yet signed a CLA. Please do so here. Once you do that we will be able to review and accept this pull request. Thanks! |
|
Ummm, I'm happy to sign the CLA, but https://www.home-assistant.io/developers/cla/ implies there should be a link above for me to do that thing, and I can't see it... |
|
@mikalstill, it looks like your link is https://home-assistant.io/developers/cla_sign_start/?pr=home-assistant/home-assistant#19732 The message from the home assistant bot had it (the word here was hyperlinked to that url). |
|
I swear that comment wasn't there before. Regardless, I'll sign the CLA now. |
|
|
||
|
|
||
| class UnknownOrangePiBoard(Exception): | ||
| """'board' config item not set""" |
To be honest, I not a big fan of suggesting to run HA as root. How was this issue solved for RPi.GPIO? |
|
|
||
| def setup(hass, base_config): | ||
| """Set up the GPIO component.""" | ||
| global GPIO_LIBRARY |
There was a problem hiding this comment.
Don't use global, we have hass.data.
There was a problem hiding this comment.
Ok, I'll go have a look at examples.
| board = ORANGEPI_BOARDS.get(board_name) | ||
| _LOGGER.info('OrangePi %s board specified', board_name) | ||
|
|
||
| if not board_name: |
There was a problem hiding this comment.
All validation should be done with the Schema.
There was a problem hiding this comment.
board_name is only required if you specify an Orange Pi, there is no equivalent concept with Raspberry Pi GPIOs. Is there an existing example of how to conditionally validate that a config value is set dependant on another config value?
Totally agreed. That said, its not an as-root requirement, its a with-permissions-for-/dev/mem requirement. That can be done with filesystem permissions if required. My understanding is that Raspberry Pi deals with this by having a kernel driver which exposes the GPIOs in a more friendly manner. I'm not opposed to doing that for OrangePi, but it will take a little time to do. |
Per review comments on PR home-assistant#19732.
|
The Rpi.GPIO library first tries /dev/gpiomem (which is Broadcom specific, and has perms granted via udev), then falls back to /dev/mem (which will require perms). We can solve the access issue for /dev/mem with some udev rules. Having said that, after some discussion around the office at Ozlabs, /dev/mem is quite dangerous, any process that can access that can effectively access the memory of any process on the system, including the kernel, and library devs who access that should be admonished^H^H^H^H taken aside and taught how to abstract access via kernel driver. In this case, we should have access to /sys/class/gpio, which in provided by the pinctrl subsystem on the Orange Pi. This library access GPIO via that: https://pypi.org/project/OPi.GPIO/ It will need a patch to support the Orange Pi Prime, but that seems pretty straightforward. |
|
It's worth noting that HassOS currently runs HomeAssistant as root, and passes /dev/mem through to the docker container (not that I recommend we take this path). I'll log this as an issue. |
|
Ok, so this latest patch does a few things:
I think that resolves all of fabaff's concerns? |
|
Hey @pvizeli, it was suggested I ping you about two things around this PR:
Thanks! |
Instead of adding a new component for orangepi (which is a raspberry pi clone), let's just extend the existing component to support more than one family of boards. I have tested this patch on a raspberry pi 3 and an orangepi prime, in both cases with a LED on a GPIO controlled switch. Note that the orangepi GPIO library requires access to /dev/mem, which I have resolved for now by running Home Assistant as root.
Per review comments on PR home-assistant#19732.
This library doesn't require root or access to /dev/mem because it uses the sysfs interface for GPIOs instead. It also doesn't need to know what OrangePi board you're using, so the configuration is simpler.
Its much less confusing, given there are two competing numbering schemes.
This update brings the documentation inline with what is proposed
in Home Assistant PR:
home-assistant/core#19732
| """Set up the GPIO component.""" | ||
| hass.data[DOMAIN] = {} | ||
|
|
||
| config = base_config.get(DOMAIN) |
There was a problem hiding this comment.
Use dict[key] for required config keys and keys with default config schema values.
| family_name = config.get(CONF_BOARD_FAMILY, DEFAULT_FAMILY) | ||
| lib_name = FAMILY_LIBRARIES.get(family_name) | ||
| _LOGGER.info('Configured to use board family %s', family_name) | ||
| _LOGGER.info('Will use %s as GPIO library', lib_name) |
There was a problem hiding this comment.
Merge these logging messages to one.
| if not lib_name: | ||
| raise UnknownBoardFamily('Unknown board family: %s' | ||
| % config.get(CONF_BOARD_FAMILY)) | ||
| hass.data[DOMAIN][LIBRARY] = importlib.import_module(lib_name) |
There was a problem hiding this comment.
Cache the imported lib in a local variable that we can use below.
| self._state = None | ||
|
|
||
| rpi_gpio.setup_input(self._port, self._pull_mode) | ||
| rpi_gpio.setup_input(self._hass, self._port, self._pull_mode) |
There was a problem hiding this comment.
Move this to entity coroutine method async_added_to_hass.
| """Represent a binary sensor that uses Raspberry Pi GPIO.""" | ||
|
|
||
| def __init__(self, name, port, pull_mode, bouncetime, invert_logic): | ||
| def __init__(self, hass, name, port, pull_mode, bouncetime, invert_logic): |
There was a problem hiding this comment.
Don't pass in hass. It will be set on the entity when it has been added to home assistant.
| """Representation of a Raspberry GPIO cover.""" | ||
|
|
||
| def __init__(self, name, relay_pin, state_pin, state_pull_mode, | ||
| def __init__(self, hass, name, relay_pin, state_pin, state_pull_mode, |
| def __init__(self, name, port, pull_mode, bouncetime, invert_logic): | ||
| def __init__(self, hass, name, port, pull_mode, bouncetime, invert_logic): | ||
| """Initialize the RPi binary sensor.""" | ||
| self._hass = hass |
| rpi_gpio.setup_output(self._relay_pin) | ||
| rpi_gpio.setup_input(self._state_pin, self._state_pull_mode) | ||
| rpi_gpio.write_output(self._relay_pin, 0 if self._invert_relay else 1) | ||
| rpi_gpio.setup_output(self._hass, self._relay_pin) |
There was a problem hiding this comment.
Move to async_added_to_hass.
| """Representation of a Raspberry Pi GPIO.""" | ||
|
|
||
| def __init__(self, name, port, invert_logic): | ||
| def __init__(self, hass, name, port, invert_logic): |
| self._state = False | ||
| rpi_gpio.setup_output(self._port) | ||
| rpi_gpio.write_output(self._port, 1 if self._invert_logic else 0) | ||
| rpi_gpio.setup_output(self._hass, self._port) |
There was a problem hiding this comment.
Move to async_added_to_hass.
|
This should not be part of the Raspberry Pi integration but instead be a new one. Over time, the integrations will diverge, and we will end up with a bunch of if…else statements. We don't want that. |
|
This PR seems to have gone stale. Closing it. |
Description:
Instead of adding a new component for orangepi (which is a raspberry pi clone), let's just extend the existing component to support more than one family of boards.
I have tested this patch on a raspberry pi 3 and an orangepi prime, in both cases with a LED on a GPIO controlled switch. Note that the orangepi GPIO library requires access to /dev/mem, which I have resolved for now by running Home Assistant as root.
I'm holding off on a home-assistant.io pull request to match this until I get a feel for if this approach is acceptable to you.
This code was implemented to support a Home Assistant tutorial running at linux.conf.au 2019 in a couple of weeks, it would be super cool to be able to have this merged by then so that attendees don't need to carry private patches.
Example entry for
configuration.yaml(if applicable):Checklist:
tox. Your PR cannot be merged unless tests passIf user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
REQUIREMENTSvariable (example).requirements_all.txtby runningscript/gen_requirements_all.py..coveragerc.If the code does not interact with devices: