Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions doc/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,28 @@ Arguments:
Used by:
- `TasmotaPowerDriver`_

TenmaSerialPort
++++++++++++++++
A :any:`TenmaSerialPort` describes a *Tenma* as supported by
`tenma-serial <https://github.com/kxtells/tenma-serial>`_.

.. code-block:: yaml

TenmaSerialPort:
match:
ID_PATH: pci-0000:00:15.0-usb-0:3.2:1.0
index: 1


The example describes port 1 on the hub with the ID_PATH
``pci-0000:00:15.0-usb-0:3.2:1.0``.

Arguments:
- match (dict): key and value pairs for a udev match, see `udev Matching`_

Used by:
- `TenmaSerialDriver`_

Digital Outputs
~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -2293,6 +2315,36 @@ Implements:
Arguments:
- delay (float, default=2.0): delay in seconds between off and on

TenmaSerialDriver
~~~~~~~~~~~~~~~~~~
A :any:`TenmaSerialDriver` controls a `TenmaSerialPort`_, allowing control of the
target power state without user interaction.

Binds to:
port:
- `TenmaSerialPort`_
- NetworkTenmaSerialPort

Implements:
- :any:`PowerProtocol`
- :any:`ResetProtocol`

.. code-block:: yaml

TenmaSerialDriver:
delay: 10.0
ovp: true
ocp: true
voltage: 12000
current: 2000

Arguments:
- delay (float, default=2.0): delay in seconds between off and on
- ovp (bool, default=False): overvoltage protection
- ocp (bool, default=False): overcurrent protection
- voltage (int, default=12000): set mV
- current (int, default=2000): set mA

TasmotaPowerDriver
~~~~~~~~~~~~~~~~~~
A :any:`TasmotaPowerDriver` controls a `TasmotaPowerPort`_, allowing the outlet
Expand Down
2 changes: 1 addition & 1 deletion labgrid/driver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .powerdriver import ManualPowerDriver, ExternalPowerDriver, \
DigitalOutputPowerDriver, YKUSHPowerDriver, \
USBPowerDriver, SiSPMPowerDriver, NetworkPowerDriver, \
PDUDaemonDriver
PDUDaemonDriver, TenmaSerialDriver
from .usbloader import MXSUSBDriver, IMXUSBDriver, BDIMXUSBDriver, RKUSBDriver, UUUDriver
from .usbsdmuxdriver import USBSDMuxDriver
from .usbsdwiredriver import USBSDWireDriver
Expand Down
76 changes: 76 additions & 0 deletions labgrid/driver/powerdriver.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import shlex
import time
import math
import ast
from importlib import import_module

import attr
Expand Down Expand Up @@ -109,6 +110,81 @@ def get(self):
return False
raise ExecutionError(f"Did not find port status in sispmctl output ({repr(output)})")

@target_factory.reg_driver
@attr.s(eq=False)
class TenmaSerialDriver(Driver, PowerResetMixin, PowerProtocol):
"""TenmaSerialDriver - Driver using a Single Output Programmable to control a
target's power using the tenma-serial tool https://github.com/kxtells/tenma-serial/"""

bindings = {"port": {"TenmaSerialPort", "NetworkTenmaSerialPort"}, }
delay = attr.ib(default=2.0, validator=attr.validators.instance_of(float))
ovp = attr.ib(default=False, validator=attr.validators.instance_of(bool))
ocp = attr.ib(default=False, validator=attr.validators.instance_of(bool))
voltage = attr.ib(default=12000, validator=attr.validators.instance_of(int))
current = attr.ib(default=2000, validator=attr.validators.instance_of(int))

def __attrs_post_init__(self):
super().__attrs_post_init__()
if self.target.env:
self.tool = self.target.env.config.get_tool('tenma-control')
else:
self.tool = 'tenma-control'

def _get_tenmaserial_prefix(self):
options = []

# overvoltage protection (bool)
if self.ovp:
options.append('--ovp-enable')
else:
options.append('--ovp-disable')

# overcurrent protection (bool)
if self.ocp:
options.append('--ocp-enable')
else:
options.append('--ocp-disable')

# set mV (int)
options.append(f'-v {self.voltage}')

# set mA (int)
options.append(f'-c {self.current}')

return self.port.command_prefix + [
self.tool,
str(self.port.path),
] + options

@Driver.check_active
@step()
def on(self):
cmd = ['--on']
processwrapper.check_output(self._get_tenmaserial_prefix() + cmd)

@Driver.check_active
@step()
def off(self):
cmd = ['--off']
processwrapper.check_output(self._get_tenmaserial_prefix() + cmd)

@Driver.check_active
@step()
def cycle(self):
self.off()
time.sleep(self.delay)
self.on()

@Driver.check_active
@step()
def get(self):
cmd = ['-S']
output = processwrapper.check_output(self._get_tenmaserial_prefix() + cmd)
status = ast.literal_eval(output.decode('utf-8').strip().splitlines()[1])
if status['outEnabled']:
return True
else:
return False

@target_factory.reg_driver
@attr.s(eq=False)
Expand Down
9 changes: 8 additions & 1 deletion labgrid/remote/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,12 @@ def power(self):
name = self.args.name
target = self._get_target(place)
from ..resource.power import NetworkPowerPort, PDUDaemonPort
from ..resource.remote import NetworkUSBPowerPort, NetworkSiSPMPowerPort, NetworkSysfsGPIO
from ..resource.remote import (
NetworkUSBPowerPort,
NetworkSiSPMPowerPort,
NetworkSysfsGPIO,
NetworkTenmaSerialPort,
)
from ..resource import TasmotaPowerPort, NetworkYKUSHPowerPort

drv = None
Expand All @@ -899,6 +904,8 @@ def power(self):
drv = self._get_driver_or_new(target, "USBPowerDriver", name=name)
elif isinstance(resource, NetworkSiSPMPowerPort):
drv = self._get_driver_or_new(target, "SiSPMPowerDriver", name=name)
elif isinstance(resource, NetworkTenmaSerialPort):
drv = self._get_driver_or_new(target, "TenmaSerialDriver", name=name)
elif isinstance(resource, PDUDaemonPort):
drv = self._get_driver_or_new(target, "PDUDaemonDriver", name=name)
elif isinstance(resource, TasmotaPowerPort):
Expand Down
22 changes: 22 additions & 0 deletions labgrid/remote/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,27 @@ def release(self, *args, **kwargs):
self.poll()


@attr.s(eq=False)
class TenmaSerialExport(ResourceExport):
def __attrs_post_init__(self):
super().__attrs_post_init__()

def _get_params(self):
"""Helper function to return parameters"""
return {
"host": self.host,
"busnum": self.local.busnum,
"devnum": self.local.devnum,
"path": self.local.path,
"vendor_id": self.local.vendor_id,
"model_id": self.local.model_id,
"index": self.local.index,
}


exports["TenmaSerialPort"] = TenmaSerialExport


@attr.s(eq=False)
class SerialPortExport(ResourceExport):
"""ResourceExport for a USB or Raw SerialPort"""
Expand Down Expand Up @@ -564,6 +585,7 @@ def __attrs_post_init__(self):
exports["USBAudioInput"] = USBAudioInputExport
exports["USBTMC"] = USBGenericExport
exports["SiSPMPowerPort"] = SiSPMPowerPortExport
exports["TenmaSerialPort"] = TenmaSerialExport
exports["USBPowerPort"] = USBPowerPortExport
exports["DeditecRelais8"] = USBDeditecRelaisExport
exports["HIDRelay"] = USBHIDRelayExport
Expand Down
1 change: 1 addition & 0 deletions labgrid/resource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
AndroidUSBFastboot,
DFUDevice,
DeditecRelais8,
TenmaSerialPort,
HIDRelay,
IMXUSBLoader,
LXAUSBMux,
Expand Down
10 changes: 10 additions & 0 deletions labgrid/resource/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,16 @@ def __attrs_post_init__(self):
super().__attrs_post_init__()


@target_factory.reg_resource
@attr.s(eq=False)
class NetworkTenmaSerialPort(RemoteUSBResource):
"""The TenmaSerialPort describes a remotely accessible tenma-contro power port"""
index = attr.ib(default=None, validator=attr.validators.instance_of(int))
def __attrs_post_init__(self):
self.timeout = 10.0
super().__attrs_post_init__()


@target_factory.reg_resource
@attr.s(eq=False)
class NetworkUSBPowerPort(RemoteUSBResource):
Expand Down
2 changes: 2 additions & 0 deletions labgrid/resource/suggest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
IMXUSBLoader,
AndroidUSBFastboot,
DFUDevice,
TenmaSerialPort,
USBSDMuxDevice,
USBSDWireDevice,
AlteraUSBBlaster,
Expand Down Expand Up @@ -45,6 +46,7 @@ def __init__(self, args):
self.resources.append(IMXUSBLoader(**args))
self.resources.append(AndroidUSBFastboot(**args))
self.resources.append(DFUDevice(**args))
self.resources.append(TenmaSerialPort(**args))
self.resources.append(USBMassStorage(**args))
self.resources.append(USBSDMuxDevice(**args))
self.resources.append(USBSDWireDevice(**args))
Expand Down
15 changes: 15 additions & 0 deletions labgrid/resource/udev.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,21 @@ def __attrs_post_init__(self):
self.match['ID_MODEL'] = 'DEDITEC_USB-OPT_REL-8'
super().__attrs_post_init__()

@target_factory.reg_resource
@attr.s(eq=False)
class TenmaSerialPort(USBResource):
"""This resource describes a tenma-serial power port"""

def __attrs_post_init__(self):
self.match['SUBSYSTEM'] = 'tty'
super().__attrs_post_init__()

@property
def path(self):
if self.device is not None:
return self.device.device_node

return None

@target_factory.reg_resource
@attr.s(eq=False)
Expand Down
4 changes: 4 additions & 0 deletions man/labgrid-device-config.5
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ See: <https://github.com/openssh/openssh\-portable>
Path to the sshfs binary, used by the SSHDriver.
See: <https://github.com/libfuse/sshfs>
.TP
.B \fBtenma\-serial\fP
Path to the tenma\-control binary, used by the TenmaSerialDriver.
See: <https://github.com/kxtells/tenma\-serial>
.TP
.B \fBuhubctl\fP
Path to the uhubctl binary, used by the USBPowerDriver.
See: <https://github.com/mvp/uhubctl>
Expand Down
5 changes: 4 additions & 1 deletion man/labgrid-device-config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ TOOLS KEYS
Path to the sshfs binary, used by the SSHDriver.
See: https://github.com/libfuse/sshfs

``tenma-serial``
Path to the tenma-control binary, used by the TenmaSerialDriver.
See: https://github.com/kxtells/tenma-serial

``uhubctl``
Path to the uhubctl binary, used by the USBPowerDriver.
See: https://github.com/mvp/uhubctl
Expand Down Expand Up @@ -250,4 +254,3 @@ SEE ALSO
--------

``labgrid-client``\(1), ``labgrid-exporter``\(1)

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ dependencies = [
"PyYAML>=6.0.1",
"requests>=2.26.0",
"xmodem>=0.4.6",
"tenma-serial~=1.1.3",
]
dynamic = ["version"] # via setuptools_scm

Expand Down
16 changes: 16 additions & 0 deletions tests/test_tenmaserial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from labgrid.resource.remote import NetworkTenmaSerialPort
from labgrid.driver.powerdriver import TenmaSerialDriver

def test_tenmaserial_create(target):
r = NetworkTenmaSerialPort(target,
name=None,
host="localhost",
busnum=0,
devnum=1,
path='0:1',
vendor_id=0x0,
model_id=0x0,
index=1,
)
d = TenmaSerialDriver(target, name=None)
assert (isinstance(d, TenmaSerialDriver))