Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
logs/
pids/
recorded_video/
tokens/

node_modules/
package-lock.json
Expand Down
19 changes: 18 additions & 1 deletion basic_bot.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# Basic Bot development configuration file.
#
# This configuration is intended for development and testing purposes of
# basic_bot and not users.
#
# See src/basic_bot/commons/config_file_schema.py for details
# on the configuration file schema.
#
# See src/basic_bot/created_files/basic_bot.yml for the configuration file
# that is created when you run `bb_create` to create your own bot.

bot_name: "basic_bot"
version: "0.1.0"

Expand Down Expand Up @@ -33,4 +44,10 @@ services:
BB_USE_ARECORD: "true"
development_env:
BB_DISABLE_RECOGNITION_PROVIDER: "true"
BB_LOG_ALL_MESSAGES: "true"
BB_LOG_ALL_MESSAGES: "true"

outbound_clients:
- name: "example_client"
uri: "wss://someec2instance.compute.amazonaws.com:5001"
identity: "example_client_1"
shared_token_file: "./tokens/example_client_1.txt"
35 changes: 11 additions & 24 deletions src/basic_bot/bb_start.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,15 @@
import shlex
import subprocess
import time
import traceback
import yaml
from jsonschema import validate, ValidationError
from jsonschema import ValidationError


from typing import Optional, Dict, List, Any


from basic_bot.commons.script_helpers.pid_files import is_pid_file_valid
from basic_bot.commons.script_helpers.log_files import get_log_time
from basic_bot.commons.config_file_schema import config_file_schema
from basic_bot.commons.config_file import read_config_file
from basic_bot.commons import constants as c


Expand Down Expand Up @@ -138,7 +136,9 @@ def start_service(
)


def start_services(config: dict[str, Any], services_filter: Optional[List[str]] = None) -> None:
def start_services(
config: dict[str, Any], services_filter: Optional[List[str]] = None
) -> None:
env_vars = config.get("env", {})
env_vars.update(config.get(f"{c.BB_ENV}_env", {}))

Expand All @@ -159,26 +159,13 @@ def start_services(config: dict[str, Any], services_filter: Optional[List[str]]

def main() -> None:
args = arg_parser.parse_args()
config = read_config_file(args.file)
validate_unique_names(config)
services_filter = None
if args.services:
services_filter = args.services.split(",")

try:
with open(args.file, "r") as f:
config = yaml.safe_load(f)
validate(config, config_file_schema)
validate_unique_names(config)

services_filter = None
if args.services:
services_filter = args.services.split(",")

start_services(config, services_filter)

except FileNotFoundError as e:
print(f"Error: File not found. {e}")
traceback.print_exc()
except yaml.YAMLError:
print(f"Error: Invalid YAML syntax: {args.file}")
except ValidationError as e:
print(f"Config file validation error: {e}")
start_services(config, services_filter)


if __name__ == "__main__":
Expand Down
30 changes: 9 additions & 21 deletions src/basic_bot/bb_stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@
import argparse
import os
import signal
import yaml
from jsonschema import validate, ValidationError

from typing import Optional, List, Any


from basic_bot.commons.script_helpers.pid_files import is_pid_file_valid
from basic_bot.commons.script_helpers.log_files import get_log_time
from basic_bot.commons.config_file_schema import config_file_schema
from basic_bot.commons.config_file import read_config_file


arg_parser = argparse.ArgumentParser(prog="bb_stop", description=__doc__)
Expand Down Expand Up @@ -79,7 +77,9 @@ def stop_service(
os.remove(pid_file)


def stop_services(config: dict[str, Any], services_filter: Optional[List[str]] = None) -> None:
def stop_services(
config: dict[str, Any], services_filter: Optional[List[str]] = None
) -> None:
for service in config["services"]:
if services_filter and service["name"] not in services_filter:
continue
Expand All @@ -93,24 +93,12 @@ def stop_services(config: dict[str, Any], services_filter: Optional[List[str]] =

def main() -> None:
args = arg_parser.parse_args()
config = read_config_file(args.file)
services_filter = None
if args.services:
services_filter = args.services.split(",")

try:
with open(args.file, "r") as f:
config = yaml.safe_load(f)
validate(config, config_file_schema)

services_filter = None
if args.services:
services_filter = args.services.split(",")

stop_services(config, services_filter)

except FileNotFoundError:
print(f"Error: File not found: {args.file}")
except yaml.YAMLError:
print(f"Error: Invalid YAML syntax: {args.file}")
except ValidationError as e:
print(f"Config file validation error: {e.message}")
stop_services(config, services_filter)


if __name__ == "__main__":
Expand Down
49 changes: 49 additions & 0 deletions src/basic_bot/commons/config_file.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
Utility function for reading and validating the basic_bot.yml configuration file.
"""

import os
import yaml
from jsonschema import validate, ValidationError
from basic_bot.commons.config_file_schema import config_file_schema
from basic_bot.commons import log


def read_config_file(file_path: str) -> dict:
"""
Reads and validates the configuration file at the given path.

Args:
file_path (str): Path to the configuration file.

Returns:
dict: The configuration data.

Raises:
FileNotFoundError: If the configuration file does not exist.
yaml.YAMLError: If there is an error parsing the YAML file.
jsonschema.ValidationError: If the configuration does not conform to the schema.
"""
try:
if not os.path.exists(file_path):
raise FileNotFoundError(f"Configuration file {file_path} does not exist.")

with open(file_path, "r") as file:
try:
config = yaml.safe_load(file)
except yaml.YAMLError as e:
raise yaml.YAMLError(f"Error parsing YAML file: {e}")

validate(instance=config, schema=config_file_schema)

except FileNotFoundError as e:
log.error(f"Error: {file_path} config file not found. {e}")
raise
except yaml.YAMLError as e:
print(f"Error: Invalid YAML syntax in {file_path}: {e}")
raise
except ValidationError as e:
print(f"Config file validation error: {e}")
raise

return config
36 changes: 36 additions & 0 deletions src/basic_bot/commons/config_file_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,41 @@
},
},
},
#
# Most central_hub clients connect to the websocket served by central_hub.
# For some applications, such as connecting to and controlling robots behind
# a firewall / restrictive router, it may be neccessary to have the bot
# connect outbound to a public facing host.
"outbound_clients": {
"type": "array",
"minItems": 0,
"items": {
"type": "object",
"required": ["name", "uri", "identity"],
"properties": {
#
# Must have unique name for each host.
"name": {"type": "string"},
#
# This is the websocket uri of the host to connect to.
# Example: "wss://mypublichost.com:5001"
"uri": {"type": "string"},
#
# This is the name that central_hub expect the bot to use
# when it sends the identity after first connecting.
# It must match the identity configured in basic_bot.yml
"identity": {"type": "string"},
#
# This is an optional file path pointing to a file that
# contains a shared secret token that central_hub sends
# to the outbound connection when it first connects.
#
# This can be used to authenticate the bot by the public
# facing host. The token must match the token configured
# via secrets on the public host.
"shared_token_file": {"type": "string"},
},
},
},
},
}
6 changes: 6 additions & 0 deletions src/basic_bot/commons/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
are also written to the log file when BB_ENV is "development" or "test".
"""

BB_CONFIG_FILE = env.env_string("BB_CONFIG_FILE", "./basic_bot.yml")
"""
Path to the basic_bot configuration file. This can be overridden to use
a different configuration file for testing or alternative deployments.
"""


BB_HUB_HOST = env.env_string("BB_HUB_HOST", "127.0.0.1")
"""
Expand Down
Loading