1-Wire Counters (DS2423) and Home Assistant

Hello good people,

I am new to HA but have been doing some basic things for a few months and all went very well. I decided to add some old hardware into the mix.

I have some 1-Wire sensors and counters. I did the configuration in configuration.yaml adding the following

sensor:
- platform: onewire

I am using the USB interface (DS9490R) on a Raspbery Pi v3. I am using the SD Image (docker)

Once I did the 1-wire config, I could read the temperature sensors (HA sees them too) and they work great. My problem comes with the counters. I have googled and searched HA with no luck.

I have looked in /sys/bus/w1/devices/ can can see the counter (denoted by starting with 1D) as well as the temperature sensors. I am pulling my hair out trying to get HA to see the counter (DS2423). I can even read the counter raw data and see it incrementing so I know the USB interface is working. It’s frustrating to be almost there and not get it to work.

Some background. The counter is part of an old system I made around 10years ago to count the led flashes on my electricity meter. I would like to get that functinality in HA and have better graphs, stats etc.

This is my last go at getting it working before trying another method (eg ESP Home). If anyone has any pointers how to get it working, I would be grateful to know.

Thanks in advance

Adding the answer to my question if anyone else needs it. It appears that connecting the USB DS9490R to the Pi works ok by adding in the config I describe above. The issue I came across is the the one wire entity can have 3 methods to access one wire ( direct via GPIO, OWFS and One wire Server).

It turns out the USB interface appears like the GPIO system. Looking into the python file for the one wire entity, the GPIO method is hardwired to temperature sensors only.

I have made my own custom entity file based on the current one wire sensor and modified it to report the count as KWh as the one wire setup I have is for an electricity meter counter ( counts the led flashes). Once I am happy with the result I will post the code but python is not my language of choice.

Here is my modified OneWire sensor.py file. Not sure if it’s the best quality code as python is not my thing.

"""Support for 1-Wire counter for a utility meter."""
# this is heavily modified to support DS2423 counters and the DS18S20 Temperature Sensors
# with the USB DS9490R one wire interface. If you use any other method this probably will need modifying.
# the DS2423 report the count at Kilo Watts as I use them on an electricity meter.

from glob import glob
import logging
import os
import time

import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
    TEMP_CELSIUS,
    POWER_KILO_WATT,
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity

_LOGGER = logging.getLogger(__name__)

CONF_MOUNT_DIR = "mount_dir"
CONF_NAMES = "names"
CONF_COUNT_KW ="kwhrs_count"

DEFAULT_MOUNT_DIR = "/sys/bus/w1/devices/"

DEVICE_SENSORS = {
    # Family : SensorType
    "1d": "power",
    "10": "temperature",    
}

SENSOR_TYPES = {
    # SensorType: [ Measured unit, Unit ]
    "power": ["power", POWER_KILO_WATT],
    "temperature": ["temperature", TEMP_CELSIUS],
	
}

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
    {
        vol.Optional(CONF_NAMES): {cv.string: cv.string},
        vol.Optional(CONF_MOUNT_DIR, default=DEFAULT_MOUNT_DIR): cv.string,
        vol.Optional(CONF_COUNT_KW, default=1000): cv.positive_int, #seems to be a standard that 1000 flashes = 1kWh      
    }
)

def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the one wire Sensors."""
    base_dir = config[CONF_MOUNT_DIR]
 
    kw_count = config[CONF_COUNT_KW]
    
    _LOGGER.debug("Initializing using %s", base_dir)

    devs = []
    device_names = {}
    if CONF_NAMES in config:
        if isinstance(config[CONF_NAMES], dict):
            device_names = config[CONF_NAMES]

    # We have a raw GPIO ow sensor on a Pi
    for device_family in DEVICE_SENSORS:
        for device_folder in glob(os.path.join(base_dir, f"{device_family}[.-]*")):
            sensor_id = os.path.split(device_folder)[1]
            device_file = os.path.join(device_folder, "w1_slave")
            _LOGGER.debug("OneWire2 Found Dev %s",sensor_id)
            _LOGGER.debug("OneWire def family -> %s,debug1-> %s",device_family,DEVICE_SENSORS.get(device_family))
            devs.append(
                OneWire2Direct(
                    device_names.get(sensor_id, sensor_id),
                    device_file,
                    DEVICE_SENSORS.get(device_family),
                    kw_count,
                )
            )

    if devs == []:
        _LOGGER.error(
            "No onewire sensor found. Check if dtoverlay=w1-gpio "
            "is in your /boot/config.txt. "
            "or the DS9490R USB interface is connected"
        )
        return
    else:
        _LOGGER.debug("OneWire2 devices %s",devs)
	
    add_entities(devs, True)


class OneWire2(Entity):
    """Implementation of an One wire Sensor."""

    def __init__(self, name, device_file, sensor_type,kw_count):
        """Initialize the sensor."""
        self._name = f"{name} {sensor_type.capitalize()}"
        self._device_file = device_file
        self._unit_of_measurement = SENSOR_TYPES[sensor_type][1]
        self._state = None
        self._value_raw = None
        self._kw_count = kw_count

    def _read_value_raw(self):
        """Read the value as it is returned by the sensor."""
        with open(self._device_file) as ds_device_file:
            lines = ds_device_file.readlines()
        return lines

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

    @property
    def state(self):
        """Return the state of the sensor."""
        return float(self._state / self._kw_count)

    @property
    def unit_of_measurement(self):
        """Return the unit the value is expressed in."""
        return self._unit_of_measurement

    @property
    def device_state_attributes(self):
        """Return the state attributes of the sensor."""
        return {"device_file": self._device_file, "raw_value": self._value_raw}

    @property
    def unique_id(self) -> str:
        """Return a unique ID."""
        return self._device_file

class OneWire2Direct(OneWire2):
    """Implementation of an One wire Sensor directly connected to RPI GPIO."""

    def update(self):
        """Get the latest data from the device."""
        value = None
        lines = self._read_value_raw()
        
        if self.unit_of_measurement == POWER_KILO_WATT:
            equals_pos = lines[2].find("YES c=")
            if equals_pos != -1:
                value_string = lines[2][equals_pos + 6 :]
                value = int(value_string)
                self._value_raw = int(value_string)
            self._state = value
        else:
            while lines[0].strip()[-3:] != "YES":
                time.sleep(0.2)
                lines = self._read_value_raw()
            equals_pos = lines[1].find("t=")
            if equals_pos != -1:
                value_string = lines[1][equals_pos + 2 :]
                value = round(float(value_string) / 1000.0, 1)
                self._value_raw = float(value_string)
            self._state = value

I placed the code in \config\custom_components\onewire2 with __init__.py and manifest.json from the original one wire installation. The code for the original I got from github at https://github.com/home-assistant/core/tree/dev/homeassistant/components/onewire

My configuration.yaml now has the following to use this custom component

sensor:
  - platform: onewire2
    kwhrs_count: 1

The kwhrs_count entry is to scale the kWh reading. Most electricity meters have 1000 led flashes = 1kWh. I put the scalling in the sensors.py but decided to do the scalling when reading the data from the database instead.

Hope fully this concludes this episode and I hope this helps out someone in the future.

Next project is integrating a current transformer to measure AC power.