Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pijuice_sys re-implementation #583

Open
wants to merge 3 commits into
base: experimental
Choose a base branch
from
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
13 changes: 13 additions & 0 deletions Software/Source/debian-base/pijuice-poweroff.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=PiJuice poweroff
Before=poweroff.target halt.target
DefaultDependencies=no

[Service]
Type=oneshot
User=pijuice
WorkingDirectory=/var/lib/pijuice/
ExecStart=/usr/bin/pijuice_sys poweroff

[Install]
WantedBy=poweroff.target halt.target
5 changes: 2 additions & 3 deletions Software/Source/debian-base/pijuice.service
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
[Unit]
Description=PiJuice status service
Description=PiJuice system daemon
After=network.target

[Service]
Type=idle
User=pijuice
WorkingDirectory=/var/lib/pijuice/
ExecStart=/usr/bin/pijuice_sys.py
ExecStopPost=/usr/bin/pijuice_sys.py stop
ExecStart=/usr/bin/pijuice_sys daemon
Restart=always

[Install]
Expand Down
Empty file.
5 changes: 5 additions & 0 deletions Software/Source/pijuice_cmd/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python3
from pijuice_cmd.cli import main

if __name__ == "__main__":
main()
205 changes: 205 additions & 0 deletions Software/Source/pijuice_cmd/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#!/usr/bin/env python3
import argparse
import os
import sys

from pijuice import PiJuice


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="PiJuice Cmd")
subparsers = parser.add_subparsers(
help="sub-command help", dest="command", required=True
)

parser.add_argument(
"-B",
"--i2c-bus",
metavar="BUS",
type=int,
default=os.environ.get("PIJUICE_I2C_BUS", 1),
help="I2C Bus (default: %(default)s)",
)
parser.add_argument(
"-A",
"--i2c-address",
metavar="ADDRESS",
type=int,
default=os.environ.get("PIJUICE_I2C_ADDRESS", 0x14),
help="I2C Address (default: %(default)s)",
)
parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="Disable output",
)

parser_wakeup_on_charge = subparsers.add_parser(
"wakeup-on-charge", help="configure wakeup-on-charge"
)
parser_wakeup_on_charge.add_argument(
"-D",
"--disabled",
action="store_true",
help="Whether to disable wakeup-on-charge",
)
parser_wakeup_on_charge.add_argument(
"-T",
"--trigger-level",
metavar="TRIGGER_LEVEL",
type=int,
default=100,
help="Battery-charge to wakeup at (default: %(default)s)",
)

parser_system_power_switch = subparsers.add_parser(
"system-power-switch", help="configure system-power-switch"
)
parser_system_power_switch.add_argument(
"state",
type=int,
choices=[0, 500, 2100],
help="Current limit for VSYS pin (in mA)",
)

def validate_power_off_delay(value):
try:
value = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"invalid int value: '{value}'")
if not 0 <= value <= 65535:
raise argparse.ArgumentTypeError(f"not within range: {value}")
return value

parser_power_off = subparsers.add_parser("power-off", help="configure power-off")
parser_power_off.add_argument(
"-D",
"--delay",
metavar="DELAY",
type=validate_power_off_delay,
default=0,
help="Delay before powering off (0 to 65535) (default: %(default)s)",
)

def validate_led_blink_count(value):
try:
value = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"invalid int value: '{value}'")
if not 0 < value < 255:
raise argparse.ArgumentTypeError(f"not within range: {value}")
return value

def validate_led_blink_rgb(value):
values = value.split(",")
if not len(values) == 3:
raise argparse.ArgumentTypeError(f"value not in format 'R,G,B': '{value}'")
try:
values = [int(v) for v in values]
except ValueError:
raise argparse.ArgumentTypeError(f"invalid int values: '{value}'")
return values

def validate_led_blink_period(value):
try:
value = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"invalid int value: '{value}'")
if not 10 <= value < 2550:
raise argparse.ArgumentTypeError(f"not within range: {value}")
return value

parser_led_blink = subparsers.add_parser("led-blink", help="run led-blink pattern")
parser_led_blink.add_argument(
"-L",
"--led",
metavar="LED",
type=str,
choices=["D1", "D2"],
default="D2",
help="Led to blink (default: %(default)s)",
)
parser_led_blink.add_argument(
"-C",
"--count",
metavar="COUNT",
type=validate_led_blink_count,
default=1,
help="Amount of repetitions (default: %(default)s)",
)
parser_led_blink.add_argument(
"--rgb1",
metavar="R,G,B",
type=validate_led_blink_rgb,
default="150,0,0",
help="RGB components of first blink (default: %(default)s)",
)
parser_led_blink.add_argument(
"--period1",
metavar="PERIOD",
type=validate_led_blink_period,
default=200,
help="Duration of first blink in milliseconds (default: %(default)s)",
)
parser_led_blink.add_argument(
"--rgb2",
metavar="R,G,B",
type=validate_led_blink_rgb,
default="0,100,0",
help="RGB components of second blink (default: %(default)s)",
)
parser_led_blink.add_argument(
"--period2",
metavar="PERIOD",
type=validate_led_blink_period,
default=200,
help="Duration of second blink in milliseconds (default: %(default)s)",
)

return parser.parse_args()


def main():
args = parse_args()
try:
pijuice = PiJuice(
bus=args.i2c_bus,
address=args.i2c_address,
)
except:
print("Failed to connect to PiJuice")
sys.exit(1)

if args.command == "wakeup-on-charge":
if args.disabled:
pijuice.power.SetWakeUpOnCharge(arg="DISABLED")
if not args.quiet:
print("Disabled wakeup-on-charge")
else:
pijuice.power.SetWakeUpOnCharge(arg=args.trigger_level)
if not args.quiet:
print(
f"Enabled wakeup-on-charge with trigger-level: {args.trigger_level}"
)
elif args.command == "system-power-switch":
pijuice.power.SetSystemPowerSwitch(state=args.state)
if not args.quiet:
print(f"Set System Power-Switch to: {args.state}mA")
elif args.command == "power-off":
pijuice.power.SetPowerOff(delay=args.delay)
if not args.quiet:
print(f"Set PowerOff with delay: {args.delay}s")
elif args.command == "led-blink":
pijuice.status.SetLedBlink(
led=args.led,
count=args.count,
rgb1=args.rgb1,
period1=args.period1,
rgb2=args.rgb2,
period2=args.period2,
)


if __name__ == "__main__":
main()
Empty file.
5 changes: 5 additions & 0 deletions Software/Source/pijuice_sys/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env python3
from pijuice_sys.cli import main

if __name__ == "__main__":
main()
128 changes: 128 additions & 0 deletions Software/Source/pijuice_sys/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env python3
import argparse
import asyncio
import logging
import pathlib
import sys
import os

from pijuice_sys.daemon import (
PiJuiceSys,
PiJuiceSysInterfaceError,
PiJuiceSysConfigValidationError,
)


logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__package__)


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="PiJuice System Daemon")
subparsers = parser.add_subparsers(
help="sub-command help", dest="command", required=True
)

parser.add_argument(
"-v",
"--verbose",
action="count",
default=0,
help="Increase verbosity level",
)
parser.add_argument(
"-B",
"--i2c-bus",
metavar="BUS",
type=int,
default=os.environ.get("PIJUICE_I2C_BUS", 1),
help="I2C Bus (default: %(default)s)",
)
parser.add_argument(
"-A",
"--i2c-address",
metavar="ADDRESS",
type=int,
default=os.environ.get("PIJUICE_I2C_ADDRESS", 0x14),
help="I2C Address (default: %(default)s)",
)
parser.add_argument(
"-C",
"--config-file",
metavar="FILE",
dest="config_file_path",
type=pathlib.Path,
default=os.environ.get(
"PIJUICE_CONFIG_FILE", "/var/lib/pijuice/pijuice_config.JSON"
),
help="Path to read Configuration file from (default: %(default)s)",
)

parser_daemon = subparsers.add_parser("daemon", help="run daemon")
parser_daemon.add_argument(
"--pid-file",
metavar="FILE",
dest="pid_file_path",
type=pathlib.Path,
default=os.environ.get("PIJUICE_PID_FILE", "/tmp/pijuice_sys.pid"),
help="Path to create pid-file at (default: %(default)s)",
)
parser_daemon.add_argument(
"--poll-interval",
metavar="INTERVAL",
type=float,
default=os.environ.get("PIJUICE_POLL_INTERVAL", 5.0),
help="Interval at which to poll status from pijuice (default: %(default)s)",
)
parser_daemon.add_argument(
"--button-poll-interval",
metavar="INTERVAL",
type=float,
default=os.environ.get("PIJUICE_BUTTON_POLL_INTERVAL", 1.0),
help="Interval at which to poll button-events from pijuice (default: %(default)s)",
)

parser_poweroff = subparsers.add_parser("poweroff", help="execute poweroff command")
return parser.parse_args()


async def run():
args = parse_args()
if args.verbose == 1:
logger.setLevel(logging.INFO)
elif args.verbose == 2:
logger.setLevel(logging.DEBUG)

try:
pijuice_sys = PiJuiceSys(
i2c_bus=args.i2c_bus,
i2c_address=args.i2c_address,
config_file_path=args.config_file_path,
)
except PiJuiceSysInterfaceError:
logger.error("failed to initialize PiJuice interface")
sys.exit(1)
except PiJuiceSysConfigValidationError as e:
logger.error(f"config validation failed: {e}")
sys.exit(1)

if args.command == "poweroff":
await pijuice_sys.execute_poweroff_command()
elif args.command == "daemon":
logger.debug("starting daemon")
await pijuice_sys.run_daemon(
pid_file_path=args.pid_file_path,
poll_interval=args.poll_interval,
button_poll_interval=args.button_poll_interval,
)


def main():
try:
asyncio.run(run())
except KeyboardInterrupt:
pass


if __name__ == "__main__":
main()
Loading