power: initial support for Klipper Devices
Signed-off-by: Pedro Lamas <pedrolamas@gmail.com>
This commit is contained in:
parent
7c8c0e715f
commit
287982acdd
|
@ -332,8 +332,9 @@ The following configuration options are available for all power device types:
|
|||
|
||||
[power device_name]
|
||||
type:
|
||||
# The type of device. Can be either gpio, rf, tplink_smartplug, tasmota
|
||||
# shelly, homeseer, homeassistant, loxonev1, or mqtt.
|
||||
# The type of device. Can be either gpio, klipper_device, rf,
|
||||
# tplink_smartplug, tasmota, shelly, homeseer, homeassistant, loxonev1,
|
||||
# or mqtt.
|
||||
# This parameter must be provided.
|
||||
off_when_shutdown: False
|
||||
# If set to True the device will be powered off when Klipper enters
|
||||
|
@ -407,16 +408,12 @@ initial_state: off
|
|||
timer:
|
||||
# A time (in seconds) after which the device will power off after being.
|
||||
# switched on. This effectively turns the device into a momentary switch.
|
||||
# This option is available for gpio, tplink_smartplug, shelly, and tasmota
|
||||
# devices. The timer may be a floating point value for gpio types, it should
|
||||
# be an integer for all other types. The default is no timer is set.
|
||||
# This option is available for gpio, klipper_device, tplink_smartplug,
|
||||
# shelly, and tasmota devices. The timer may be a floating point value
|
||||
# for gpio types, it should be an integer for all other types. The
|
||||
# default is no timer is set.
|
||||
```
|
||||
|
||||
!!! Note
|
||||
Moonraker can only be used to toggle host device GPIOs (ie: GPIOs on your
|
||||
PC or SBC). Moonraker cannot control GPIOs on an MCU, Klipper should be
|
||||
used for this purpose.
|
||||
|
||||
Examples:
|
||||
|
||||
```ini
|
||||
|
@ -443,6 +440,42 @@ pin: gpiochip0/gpio17
|
|||
initial_state: on
|
||||
```
|
||||
|
||||
#### Klipper Device Configuration
|
||||
|
||||
The following options are available for `klipper_device` device types:
|
||||
|
||||
```ini
|
||||
# moonraker.conf
|
||||
|
||||
object_name: output_pin my_pin
|
||||
# The Klipper object_name (as defined in your Klipper config). Valid examples:
|
||||
# output_pin my_pin
|
||||
# This parameter must be provided for "klipper_device" type devices.
|
||||
# Currently, only `output_pin` Klipper devices are supported.
|
||||
timer:
|
||||
# A time (in seconds) after which the device will power off after being.
|
||||
# switched on. This effectively turns the device into a momentary switch.
|
||||
# This option is available for gpio, klipper_device, tplink_smartplug,
|
||||
# shelly, and tasmota devices. The timer may be a floating point value
|
||||
# for gpio types, it should be an integer for all other types. The
|
||||
# default is no timer is set.
|
||||
```
|
||||
|
||||
!!! Note
|
||||
These devices cannot be used to toggle Klipper's power supply as they
|
||||
require Klipper to actually be running.
|
||||
|
||||
Examples:
|
||||
|
||||
```ini
|
||||
# moonraker.conf
|
||||
|
||||
# Control a relay providing power to the printer
|
||||
[power my_pin]
|
||||
type: klipper_device
|
||||
object_name: output_pin my_pin
|
||||
```
|
||||
|
||||
#### RF Device Configuration
|
||||
|
||||
The following options are available for gpio controlled `rf` device types:
|
||||
|
@ -466,9 +499,10 @@ initial_state: off
|
|||
timer:
|
||||
# A time (in seconds) after which the device will power off after being.
|
||||
# switched on. This effectively turns the device into a momentary switch.
|
||||
# This option is available for gpio, tplink_smartplug, shelly, and tasmota
|
||||
# devices. The timer may be a floating point value for gpio types, it should
|
||||
# be an integer for all other types. The default is no timer is set.
|
||||
# This option is available for gpio, klipper_device, tplink_smartplug,
|
||||
# shelly, and tasmota devices. The timer may be a floating point value
|
||||
# for gpio types, it should be an integer for all other types. The
|
||||
# default is no timer is set.
|
||||
on_code:
|
||||
off_code:
|
||||
# Valid binary codes that are sent via the RF transmitter.
|
||||
|
|
|
@ -44,6 +44,7 @@ class PrinterPower:
|
|||
logging.info(f"Power component loading devices: {prefix_sections}")
|
||||
dev_types = {
|
||||
"gpio": GpioDevice,
|
||||
"klipper_device": KlipperDevice,
|
||||
"tplink_smartplug": TPLinkSmartPlug,
|
||||
"tasmota": Tasmota,
|
||||
"shelly": Shelly,
|
||||
|
@ -514,6 +515,105 @@ class GpioDevice(PowerDevice):
|
|||
self.timer_handle.cancel()
|
||||
self.timer_handle = None
|
||||
|
||||
class KlipperDevice(PowerDevice):
|
||||
def __init__(self,
|
||||
config: ConfigHelper,
|
||||
initial_val: Optional[int] = None
|
||||
) -> None:
|
||||
if config.getboolean('off_when_shutdown', None) is not None:
|
||||
raise config.error(
|
||||
"Option 'off_when_shutdown' in section "
|
||||
f"[{config.get_name()}] is unsupported for 'klipper_device'")
|
||||
if config.getboolean('klipper_restart', None) is not None:
|
||||
raise config.error(
|
||||
"Option 'klipper_restart' in section "
|
||||
f"[{config.get_name()}] is unsupported for 'klipper_device'")
|
||||
super().__init__(config)
|
||||
self.off_when_shutdown = False
|
||||
self.klipper_restart = False
|
||||
self.timer: Optional[float] = config.getfloat('timer', None)
|
||||
if self.timer is not None and self.timer < 0.000001:
|
||||
raise config.error(
|
||||
f"Option 'timer' in section [{config.get_name()}] must "
|
||||
"be above 0.0")
|
||||
self.timer_handle: Optional[asyncio.TimerHandle] = None
|
||||
self.object_name = config.get('object_name', '')
|
||||
if not self.object_name.startswith("output_pin "):
|
||||
raise config.error(
|
||||
"Currently only Klipper 'output_pin' objects supported for "
|
||||
f"'object_name' in section [{config.get_name()}]")
|
||||
|
||||
self.server.register_event_handler(
|
||||
"server:status_update", self._status_update)
|
||||
self.server.register_event_handler(
|
||||
"server:klippy_ready", self._handle_ready)
|
||||
self.server.register_event_handler(
|
||||
"server:klippy_disconnect", self._handle_disconnect)
|
||||
|
||||
def _status_update(self, data: Dict[str, Any]) -> None:
|
||||
self._set_state_from_data(data)
|
||||
|
||||
async def _handle_ready(self) -> None:
|
||||
kapis: APIComp = self.server.lookup_component('klippy_apis')
|
||||
sub: Dict[str, Optional[List[str]]] = {self.object_name: None}
|
||||
try:
|
||||
data = await kapis.subscribe_objects(sub)
|
||||
self._set_state_from_data(data)
|
||||
except self.server.error as e:
|
||||
logging.info(f"Error subscribing to {self.object_name}")
|
||||
|
||||
async def _handle_disconnect(self) -> None:
|
||||
self._set_state("init")
|
||||
|
||||
def process_klippy_shutdown(self) -> None:
|
||||
self._set_state("init")
|
||||
|
||||
def refresh_status(self) -> None:
|
||||
pass
|
||||
|
||||
async def set_power(self, state) -> None:
|
||||
if self.timer_handle is not None:
|
||||
self.timer_handle.cancel()
|
||||
self.timer_handle = None
|
||||
try:
|
||||
kapis: APIComp = self.server.lookup_component('klippy_apis')
|
||||
object_name = self.object_name[len("output_pin "):]
|
||||
object_name_value = "1" if state == "on" else "0"
|
||||
await kapis.run_gcode(
|
||||
f"SET_PIN PIN={object_name} VALUE={object_name_value}")
|
||||
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
|
||||
self._check_timer()
|
||||
|
||||
def _set_state_from_data(self, data) -> None:
|
||||
if self.object_name not in data:
|
||||
return
|
||||
is_on = data.get(self.object_name, {}).get('value', 0.0) > 0.0
|
||||
state = "on" if is_on else "off"
|
||||
self._set_state(state)
|
||||
|
||||
def _set_state(self, state) -> None:
|
||||
last_state = self.state
|
||||
self.state = state
|
||||
if last_state != state:
|
||||
self.notify_power_changed()
|
||||
|
||||
def _check_timer(self):
|
||||
if self.state == "on" and self.timer is not None:
|
||||
event_loop = self.server.get_event_loop()
|
||||
power: PrinterPower = self.server.lookup_component("power")
|
||||
self.timer_handle = event_loop.delay_callback(
|
||||
self.timer, power.set_device_power, self.name, "off")
|
||||
|
||||
def close(self) -> None:
|
||||
if self.timer_handle is not None:
|
||||
self.timer_handle.cancel()
|
||||
self.timer_handle = None
|
||||
|
||||
class RFDevice(GpioDevice):
|
||||
|
||||
# Protocol definition
|
||||
|
|
Loading…
Reference in New Issue