Starting point for serial write and read

Hi there,

Can anyone provide me some basic Serial config and write/read samples for developing a custom intgeration? I would like to communicate via Serial port with an Arduino based board. I really would like to know how I can simply write and read to serial port from my custom integration.

Since i used to run my HA on a virtual HyperV machine.
This limited my options, as a serial port wasn’t available. I also didn’t want to end up pulling long cables either.
So I used serial2tcp and i ended up sending/receiving tcp with a EspHome module flashed with JeeLab

my code:

I would look at an existing component that does serial, like Serial - Home Assistant

Or maybe that integration will do what you want?

Hi, thanks for your replies!

Let me clarify a bit what I would like to achieve. I made this RF USB dongle to wirelessly control mechanical home ventilations.

This will be connected to my Raspberry Pi and shows up as a virtual com port. To communicate with this USB dongle, I need to implement serial read and write commands. I would like to make my own custom HA integration for this. I already looked at: Link. But I think here it is not clear how to write to serial port?

At this moment, the USB dongle runs Firmata to communicate with HA, not optimal.

Yes I think that the serial integration is a sensor only, and therefore only reads serial. However it uses pyserial-asyncio so that probably gives a hint.

Firmata is the standard way of serial comms with an arduino type device. If you are having problems (you haven’t said in what way it is “not optimal”) why not post a github issue?

The idea of Firmata is to have control over Arduino’s IO’s in HA, that is not what I want to achieve, I would like to control and read out the actual the fan speed, this is done by sending RF messages with my onboard RF chip and report back to HA via Firmata. Currently I have to use the Firmata “light” entity for this, since I have to have an analog value. Instead I would like to use the FAN entity. Besides it seems that my implementation seems to lose connection after a few days for some reason, cannot find the reason for that. It might be much simpler to implement lightweight response/request communication protocol for sending/requesting FAN speed.

I thought it should be doable to create my own custom integration, since there are other integrations using serial read/write as well.

Frankly esphome would have been easier.

Problaby, but I would like to do it in python. In the meantime I figured out that I need serial_asyncio.

In the meantime, I know how to send and receive data on the Serial port. What I’m still not sure about, how to implement a simple FAN entity. How can I then see the entity attribute?

Thanks, my simplified code now looks like this:

import json
import logging

import asyncio
import serial_asyncio
import voluptuous as vol

from homeassistant.components.fan import (FanEntity, PLATFORM_SCHEMA, SUPPORT_SET_SPEED, ATTR_PERCENTAGE )
from homeassistant.const import (CONF_NAME, STATE_OFF, STATE_ON, STATE_UNKNOWN)
from homeassistant.core import callback

from homeassistant.const import CONF_NAME, CONF_VALUE_TEMPLATE, EVENT_HOMEASSISTANT_STOP
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)

CONF_SERIAL_PORT = "serial_port"
CONF_BAUDRATE = "baudrate"

DEFAULT_NAME = "USB Dongle"
DEFAULT_BAUDRATE = 115200

CONF_UNIQUE_ID = 'unique_id'
CONF_DEVICE_CODE = 'device_code'
CONF_CONTROLLER_DATA = "controller_data"
CONF_DELAY = "delay"
CONF_POWER_SENSOR = 'power_sensor'
ATTR_PERCENTAGE = "percentage"

#******************************************************************************************#
#                                                                                          #
#                            Params                                                        #
#                                                                                          #
#******************************************************************************************#
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Required(CONF_SERIAL_PORT): cv.string,
        vol.Optional(CONF_BAUDRATE, default=DEFAULT_BAUDRATE): cv.positive_int,
        vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
        vol.Optional(CONF_VALUE_TEMPLATE): cv.template,
    }
)
#******************************************************************************************#
#                                                                                          #
#                            Setup                                                         #
#                                                                                          #
#******************************************************************************************#
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
    """Set up the Serial fan platform."""
    name = config.get(CONF_NAME)
    port = config.get(CONF_SERIAL_PORT)
    baudrate = config.get(CONF_BAUDRATE)

    value_template = config.get(CONF_VALUE_TEMPLATE)
    if value_template is not None:
        value_template.hass = hass

    fan = FanEntity(name, port, baudrate, value_template)
    
    async_add_entities([fan], True)

#******************************************************************************************#
#                                                                                          #
#                            FAN CLASS                                                     #
#                                                                                          #
#******************************************************************************************#
class FanEntity(Entity):
    """Representation of a Serial fan."""

    def __init__(self, name, port, baudrate, value_template):
        """Initialize the Serial fan."""
        self._name = name
        self._attr_unique_id = "123456"
        self._state = None
        self._attr_percentage = None
        self._port = port
        self._baudrate = baudrate
        self._serial_write_task = None
        self._serial_read_task = None
        self._template = value_template
        self._attributes = []
        self._last_on_speed = 2
        self._speed = 1
    
    async def async_added_to_hass(self):
        """Handle when an entity is about to be added to Home Assistant."""

    @property
    def unique_id(self):
        """Return a unique ID."""
        return self._attr_unique_id

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

    @property
    def should_poll(self):
        """No polling needed."""
        return False

    @property
    def state(self):
        """Return the state of the fan."""
        return self._state

    @property
    def percentage(self):
        """Return speed percentage of the fan."""
        return self._attr_percentage

    @property
    def extra_state_attributes(self):
        """Platform specific attributes."""
        return {
            'last_on_speed': self._last_on_speed,
        }

I can see the attribute “last_on_speed” in my automations, but I would like to see (and control) the percentage as well, what can be wrong here?

Your FanEntity class is not inheriting FanEntity but Entity, therefore your percentage property does not exist on Entity to override. Your class definition should be:

class FanEntity(FanEntity):
    """Representation of a Serial fan."""
....

Thanks for the hint, and sorry for late reply. Still it doensn’t work. I would really like to see a very basic implementation of the fan entity.

Look at this core integration for an example of a fan entity.

1 Like

Hi Marcelh,
could you tell me, how you reach the sending and receiving data on the Serial port?
I have exactly the same issue like you - I want my own integration for my own device, which communicates with UART.
I have started with this example integraion
detailed_hello_world_push
and try now to customize this for me with serial communication.
I would be very happy, if you (or someone) could help me! :slight_smile:

Try taking a look at the existing code, eg

Thank you for your answer!!
I have copied the 3 files in my custom_components folder, a new folder “serial”.
Added

# Example configuration.yaml entry
  sensor:
    - platform: serial
      serial_port: /dev/serial0

in the configuration.yaml and restart HA.

After I send something via UART, I get the following Log:

Logger: homeassistant.components.serial.sensor
Source: components/serial/sensor.py:202
Integration: serial (documentation, issues)
First occurred: 01:11:55 (1 occurrences)
Last logged: 01:11:55

Error while reading serial device /dev/serial0: device reports readiness to read but returned no data (device disconnected or multiple access on port?)
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/serial/sensor.py", line 202, in serial_read
    line = await reader.readline()
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 525, in readline
    line = await self.readuntil(sep)
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 617, in readuntil
    await self._wait_for_data('readuntil')
  File "/usr/local/lib/python3.10/asyncio/streams.py", line 502, in _wait_for_data
    await self._waiter
  File "/usr/local/lib/python3.10/site-packages/serial_asyncio/__init__.py", line 115, in _read_ready
    data = self._serial.read(self._max_read_size)
  File "/usr/local/lib/python3.10/site-packages/serial/serialposix.py", line 595, in read
    raise SerialException(
serial.serialutil.SerialException: device reports readiness to read but returned no data (device disconnected or multiple access on port?)

I found out that the port is already used by nodered.
(My “Integration” is currently in nodered and this used the port).
When I stop nodered (also don’t start with boot), the error ’ device reports readiness to read but returned no data’ isn’t there anymore.

Now I try to find a serial-message I send, but I can’t find it…
(I changed the Baudrate in the sensor.py and the rest of the settings should work for me.)
I think the line CONF_SERIAL_PORT = “serial_port” says, that the serial_port: /dev/serial0 from the configuration.yaml should be used - am I correct?

Could you help me again?

I just noticed that neither the message
"Unable to connect to the serial device %s: %s. Will retry"
nor the message
"Serial device %s connected"
are in my System Log… :confused:

Do you have a version in your manifest file?

I had exactly use the manifest from homeassistant/components/serial and there wasn’t a version.

I added a version now, but it’s the same behavior (no Log - messages)