power: add RF transmitter support

Signed-off: Daniel Bauer <github@dbauer.me>
This commit is contained in:
danijoo 2021-10-01 07:45:15 +01:00 committed by Eric Callahan
parent 86764657cf
commit c3f1b290f8
2 changed files with 61 additions and 6 deletions

View File

@ -190,7 +190,7 @@ gcode:
``` ```
## `[power]` ## `[power]`
Enables device power control. Currently GPIO (relays), TPLink Smartplug, Enables device power control. Currently GPIO (relays), RF transmitter, TPLink Smartplug,
and Tasmota (via http) devices, HomeAssistant switch are supported. and Tasmota (via http) devices, HomeAssistant switch are supported.
```ini ```ini
@ -198,7 +198,7 @@ and Tasmota (via http) devices, HomeAssistant switch are supported.
[power device_name] [power device_name]
type: gpio type: gpio
# The type of device. Can be either gpio, tplink_smartplug, tasmota # The type of device. Can be either gpio, rf, tplink_smartplug, tasmota
# shelly, homeseer, homeassistant, or loxonev1. # shelly, homeseer, homeassistant, or loxonev1.
# This parameter must be provided. # This parameter must be provided.
off_when_shutdown: False off_when_shutdown: False
@ -217,7 +217,7 @@ restart_delay: 1.
# If "restart_klipper_when_powered" is set, this option specifies the amount # If "restart_klipper_when_powered" is set, this option specifies the amount
# of time (in seconds) to delay the restart. Default is 1 second. # of time (in seconds) to delay the restart. Default is 1 second.
pin: gpiochip0/gpio26 pin: gpiochip0/gpio26
# The pin to use for GPIO devices. The chip is optional, if left out # The pin to use for GPIO and RF devices. The chip is optional, if left out
# then the module will default to gpiochip0. If one wishes to invert # then the module will default to gpiochip0. If one wishes to invert
# the signal, a "!" may be prefixed to the pin. Valid examples: # the signal, a "!" may be prefixed to the pin. Valid examples:
# gpiochip0/gpio26 # gpiochip0/gpio26
@ -297,6 +297,12 @@ output_id:
# The output_id is the name of a programmed output, virtual input or virtual # The output_id is the name of a programmed output, virtual input or virtual
# output in the loxone config his output_id (name) may only be used once in # output in the loxone config his output_id (name) may only be used once in
# the loxone config # the loxone config
on_code:
off_code:
# The above options are used for "rf" devices. The
# codes should be valid binary codes that are send via the RF transmitter.
# For example: 1011.
``` ```
Below are some potential examples: Below are some potential examples:
```ini ```ini
@ -559,4 +565,4 @@ default_qos: 0
api_qos: api_qos:
# The QOS level to use for the API topics. If not provided, the # The QOS level to use for the API topics. If not provided, the
# value specified by "default_qos" will be used. # value specified by "default_qos" will be used.
``` ```

View File

@ -12,6 +12,7 @@ import json
import struct import struct
import socket import socket
import asyncio import asyncio
import time
from tornado.iostream import IOStream from tornado.iostream import IOStream
from tornado.httpclient import AsyncHTTPClient from tornado.httpclient import AsyncHTTPClient
from tornado.escape import json_decode from tornado.escape import json_decode
@ -68,7 +69,8 @@ class PrinterPower:
"shelly": Shelly, "shelly": Shelly,
"homeseer": HomeSeer, "homeseer": HomeSeer,
"homeassistant": HomeAssistant, "homeassistant": HomeAssistant,
"loxonev1": Loxonev1 "loxonev1": Loxonev1,
"rf": RFDevice
} }
try: try:
for section in prefix_sections: for section in prefix_sections:
@ -79,7 +81,7 @@ class PrinterPower:
if dev_class is None: if dev_class is None:
raise config.error(f"Unsupported Device Type: {dev_type}") raise config.error(f"Unsupported Device Type: {dev_type}")
dev = dev_class(cfg) dev = dev_class(cfg)
if isinstance(dev, GpioDevice): if isinstance(dev, GpioDevice) or isinstance(dev, RFDevice):
if not HAS_GPIOD: if not HAS_GPIOD:
continue continue
dev.configure_line(cfg, self.chip_factory) dev.configure_line(cfg, self.chip_factory)
@ -470,6 +472,53 @@ class GpioDevice(PowerDevice):
def close(self) -> None: def close(self) -> None:
self.line.release() self.line.release()
class RFDevice(GpioDevice):
# Protocol definition
# [1, 3] means HIGH is set for 1x pulse_len and LOW for 3x pulse_len
ZERO_BIT = [1, 3] # zero bit
ONE_BIT = [3, 1] # one bit
SYNC_BIT = [1, 31] # sync between
PULSE_LEN = 0.00035 # length of a single pulse
RETRIES = 10 # send the code this many times
def __init__(self, config: ConfigHelper):
super().__init__(config)
self.on = config.get("on_code").zfill(24)
self.off = config.get("off_code").zfill(24)
def initialize(self) -> None:
self.set_power("on" if self.initial_state else "off")
def _transmit_digit(self, waveform) -> None:
self.line.set_value(1)
time.sleep(waveform[0]*RFDevice.PULSE_LEN)
self.line.set_value(0)
time.sleep(waveform[1]*RFDevice.PULSE_LEN)
def _transmit_code(self, code) -> None:
for _ in range(RFDevice.RETRIES):
for i in code:
if i == "1":
self._transmit_digit(RFDevice.ONE_BIT)
elif i == "0":
self._transmit_digit(RFDevice.ZERO_BIT)
self._transmit_digit(RFDevice.SYNC_BIT)
def set_power(self, state) -> None:
try:
if state == "on":
code = self.on
else:
code = self.off
self._transmit_code(code)
except Exception:
self.state = "error"
msg = f"Error Toggling Device Power: {self.name}"
logging.exception(msg)
raise self.server.error(msg) from None
self.state = state
# This implementation based off the work tplink_smartplug # This implementation based off the work tplink_smartplug
# script by Lubomir Stroetmann available at: # script by Lubomir Stroetmann available at: