diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 00000000..41167050
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,36 @@
+name: Upload Python Package
+
+on: push
+
+# on:
+# workflow_dispatch:
+# push:
+# tags:
+# - v**
+
+jobs:
+ docs:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Set up Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: "3.11"
+ - name: Install dependencies
+ run: |
+ python3 -m pip install -r requirements.txt
+ python3 -m pip install -r mkdocs_requirements.txt
+ python3 -m pip install -e .
+ - name: Build docs
+ run: mkdocs build -v
+ - name: Analyze
+ run: |
+ pwd
+ ls -lha
+ - name: Deploy gh-pages
+ uses: JamesIves/github-pages-deploy-action@v4.2.5
+ with:
+ branch: gh-pages
+ folder: site
diff --git a/.gitignore b/.gitignore
index 5d8885ee..c90ab088 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ detector/
*.env
*.swp
site/
+docs/reference/
.nicegui/
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..c0b2af1e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,128 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+nicegui@zauberzeug.com.
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
diff --git a/README.md b/README.md
index cccd0756..a0cea9a5 100644
--- a/README.md
+++ b/README.md
@@ -2,65 +2,8 @@
# Zauberzeug Field Friend
-This is the full source code of the [Field Friend](http://feldfreund.de) (aka Feldfreund) weeding robot.
+This is the full source code of the [Field Friend](http://feldfreund.de) (aka Feldfreund) agricultural robot.
The software is based on [RoSys](https://rosys.io) and fully Open Source.
The hardware is build by [Zauberzeug](http://zauberzeug.com) and intended as a platform to advance organic and regenerative agriculture.
-## Features
-
-- full control via web interface
-- manual steering with touch-joystick and keyboard
-- camera/motor calibration for real world coordinate system (unit: meters)
-- ...
-
-## Getting Started
-
-Run these commands on your local machine to start the Field Friend simulation:
-
-```bash
-git clone git@github.com:zauberzeug/field_friend.git
-cd field_friend
-python3 -m pip install -r requirements.txt
-./main.py
-```
-
-This will open the user interface with a simulated robot in your browser.
-
-NOTE: The software is intended to run on Linux and Unix systems.
-If you are using windows please consider running in a Docker container or virtual machine.
-
-## On Real Hardware
-
-### Development
-
-The following instructions will only work if you have a real "Zauberzeug Field Friend" at your disposal.
-Contact [sales@zauberzeug.com](mailto:sales@zauberzeug.com) if you are interested in a non-profit purchase of this development hardware.
-
-#### Setup
-
-1. ensure you can login via ssh without providing a password (via `ssh-copy-id` command)
-2. ensure you have [LiveSync](https://github.com/zauberzeug/livesync) installed with
`python3 -m pip install livesync`
-3. ensure the latest version of the docker image is installed on the Field Friend by syncing the code as described below and then running
`./docker.sh uppull`
-4. Optional: ensure the correct docker containers are loaded on startup by running
`./docker.sh stopall && ./docker.sh uppull && ./docker.sh install`
-5. Optional: update the [Lizard](https://lizard.dev) microcontroller firmware on your Robot Brain by accessing the Field Friend web interface and navigating to the "Developer" options
-
-#### Deploy and Change Code
-
-1. go to your local `field_friend` folder and start the [LiveSync](https://github.com/zauberzeug/livesync) script:
- `./sync.py `
-2. this will deploy your local code to the Field Friend
-3. as long as [LiveSync](https://github.com/zauberzeug/livesync) is active, all code change are automatically pushed to the machine
-4. any code changes will automatically trigger a reload on the Field Friend
-
-### Update RoSys and NiceGUI
-
-To utilize personal versions of RoSys and NiceGUI instead of the default ones provided in the docker image,
-modify the `sync.py` file by uncommenting the specific folders.
-
-### Debugging
-
-You can see the current log with
-
-```bash
-./docker.sh l rosys
-```
+Please see the [documentation](https://docs.feldfreund.de) for details on installation, setup and usage.
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 00000000..b9c007c3
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,85 @@
+# Contributing
+
+We're thrilled that you're interested in contributing to the Field Friend source code!
+Here are some guidelines that will help you get started.
+
+## Reporting issues
+
+If you encounter a bug or other issue, the best way to report it is by opening a new issue on our [GitHub repository](https://github.com/zauberzeug/field_friend).
+When creating the issue, please provide a clear and concise description of the problem, including any relevant error messages and code snippets.
+If possible, include steps to reproduce the issue.
+
+## Code of Conduct
+
+We follow a [Code of Conduct](https://github.com/zauberzeug/field_friend/blob/main/CODE_OF_CONDUCT.md) to ensure that everyone who participates in the NiceGUI community feels welcome and safe.
+By participating, you agree to abide by its terms.
+
+## Contributing code
+
+We are excited that you want to contribute code to the Field Friend source code.
+We're always looking for bug fixes, performance improvements, and new features.
+
+## Coding Style Guide
+
+### Formatting
+
+We use [autopep8](https://github.com/hhatto/autopep8) with a 120 character line length to format our code.
+Before submitting a pull request, please run
+
+```bash
+autopep8 --max-line-length=120 --in-place --recursive .
+```
+
+on your code to ensure that it meets our formatting guidelines.
+Alternatively you can use VSCode, open the field_friend.code-workspace file and install the recommended extensions.
+Then the formatting rules are applied whenever you save a file.
+
+In our point of view, the Black formatter is sometimes a bit too strict.
+There are cases where one or the other arrangement of, e.g., function arguments is more readable than the other.
+Then we like the flexibility to either put all arguments on separate lines or only put the lengthy event handler
+on a second line and leave the other arguments as they are.
+
+### Imports
+
+We use [ruff](https://docs.astral.sh/ruff/) to automatically sort imports:
+
+```bash
+ruff check . --fix
+```
+
+### Single vs Double Quotes
+
+Regarding single or double quotes: [PEP 8](https://peps.python.org/pep-0008/) doesn't give any recommendation, so we simply chose single quotes and sticked with it.
+On qwerty keyboards it's a bit easier to type, is visually less cluttered, and it works well for strings containing double quotes from the English language.
+
+### F-Strings
+
+We use f-strings where ever possible because they are generally more readable - once you get used to them.
+There are only a few places in the code base where performance really matters and f-strings might not be the best choice.
+These places should be marked with a `# NOTE: ...` comment when diverging from f-string usage.
+
+## Documentation
+
+### Formatting
+
+Because it has [numerous benefits](https://nick.groenen.me/notes/one-sentence-per-line/) we write each sentence in a new line.
+
+### Examples
+
+Each example should be about one concept.
+Please try to make them as minimal as possible to show what is needed to get some kind of functionality.
+We are happy to merge pull requests with new examples which show new concepts, ideas or interesting use cases.
+
+## Pull requests
+
+To get started, fork the repository on GitHub, clone it somewhere on your filesystem, commit and push your changes,
+and then open a pull request (PR) with a detailed description of the changes you've made
+(the PR button is shown on the GitHub website of your forked repository).
+
+When submitting a PR, please make sure that the code follows the existing coding style and that all tests are passing.
+If you're adding a new feature, please include tests that cover the new functionality.
+
+## Thank you!
+
+Thank you for your interest in contributing.
+We're looking forward to work with you!
diff --git a/docs/features/field_planner.md b/docs/features/field_planner.md
new file mode 100644
index 00000000..2c3f3992
--- /dev/null
+++ b/docs/features/field_planner.md
@@ -0,0 +1,3 @@
+# Field Planner
+
+The Field Planner is a tool to create and manage fields and routes for the Field Friend.
diff --git a/docs/features/user_interface.md b/docs/features/user_interface.md
new file mode 100644
index 00000000..7237eba3
--- /dev/null
+++ b/docs/features/user_interface.md
@@ -0,0 +1,4 @@
+# User Interface
+
+The Field Friend user interface is a web application build with [NiceGUI](https://nicegui.io).
+This allows you to control the robot via the local "Feldfreund" WiFi hotspot or remotely via NiceGUI On Air.
diff --git a/docs/generate_reference.py b/docs/generate_reference.py
new file mode 100644
index 00000000..f9a681df
--- /dev/null
+++ b/docs/generate_reference.py
@@ -0,0 +1,73 @@
+import dataclasses
+import importlib
+import inspect
+import logging
+import sys
+from pathlib import Path
+from types import ModuleType
+
+import mkdocs_gen_files
+
+nav = mkdocs_gen_files.Nav()
+
+
+def extract_events(filepath: str) -> dict[str, str]:
+ with open(filepath, 'r') as f:
+ lines = f.read().splitlines()
+ events = {}
+ for l, line in enumerate(lines):
+ if line.endswith('= Event()'):
+ event_name = line.strip().split()[0].removeprefix('self.')
+ event_doc = lines[l+1].split('"""')[1]
+ events[event_name] = event_doc
+ return events
+
+
+for path in sorted(Path('.').rglob('__init__.py')):
+ identifier = str(path.parent).replace('/', '.')
+ if identifier in ['field_friend',]:
+ continue
+
+ try:
+ module = importlib.import_module(identifier)
+ except Exception:
+ logging.warning(f'Failed to import {identifier}')
+ continue
+
+ doc_path = path.parent.with_suffix('.md')
+ found_something = False
+ for name in getattr(module, '__all__', dir(module)):
+ if name.startswith('_'):
+ continue # skip private fields
+ cls = getattr(module, name)
+ if isinstance(cls, ModuleType):
+ continue # skip sub-modules
+ if dataclasses.is_dataclass(cls):
+ continue # skip dataclasses
+ if not cls.__doc__:
+ continue # skip classes without docstring
+ try:
+ events = extract_events(inspect.getfile(cls))
+ except Exception:
+ logging.warning(f'skipping {identifier}.{name}')
+ continue
+ with mkdocs_gen_files.open(Path('reference', doc_path), 'a') as fd:
+ print(f'::: {identifier}.{name}', file=fd)
+ if events:
+ print(' options:', file=fd)
+ print(' filters:', file=fd)
+ for event_name in events:
+ print(f' - "!{event_name}"', file=fd)
+ print('### Events', file=fd)
+ print('Name | Description', file=fd)
+ print('- | -', file=fd)
+ for event_name, event_doc in events.items():
+ print(f'{event_name} | {event_doc}', file=fd)
+ print('', file=fd)
+ found_something = True
+
+ if found_something:
+ nav[path.parent.parts[1:]] = doc_path.as_posix()
+
+with mkdocs_gen_files.open('reference/SUMMARY.md', 'w') as nav_file:
+ nav_file.writelines(nav.build_literate_nav())
diff --git a/docs/getting_started.md b/docs/getting_started.md
new file mode 100644
index 00000000..65b58b2a
--- /dev/null
+++ b/docs/getting_started.md
@@ -0,0 +1,60 @@
+# Getting Started
+
+## Run in Simulation
+
+We suggest you begin with simulating the Field Friend on your local development machine.
+The software is meant to run on Linux and Unix systems so if you are using Windows, consider running in a Docker container or virtual machine.
+
+Just execute the following commands:
+
+```bash
+git clone git@github.com:zauberzeug/field_friend.git
+cd field_friend
+python3 -m pip install -r requirements.txt
+./main.py
+```
+
+This will open the user interface of a simulated robot in your browser.
+If you change some code, the simulation will automatically reload.
+The Field Friend code is based on [RoSys](https://rosys.io) which itself uses [NiceGUI](https://nicegui.io),
+both having a very gentle learning curve and are designed to boost your rapid development and testing.
+
+## Run on Real Hardware
+
+The following instructions will only work if you have a real Zauberzeug Field Friend at your disposal.
+Contact [sales@zauberzeug.com](mailto:sales@zauberzeug.com) if you are interested in purchasing this robot.
+
+### Setup
+
+1. ensure you can login via ssh without providing a password (via `ssh-copy-id` command)
+2. ensure you have [LiveSync](https://github.com/zauberzeug/livesync) installed with
`python3 -m pip install livesync`
+3. ensure the latest version of the docker image is installed on the Field Friend by syncing the code as described below and then running
`./docker.sh uppull`
+4. Optional: ensure the correct docker containers are loaded on startup by running
`./docker.sh stopall && ./docker.sh uppull && ./docker.sh install`
+5. Optional: update the [Lizard](https://lizard.dev) microcontroller firmware on your Robot Brain by accessing the Field Friend web interface and navigating to the "Developer" options
+
+### Deploy and Change Code
+
+1. go to your local `field_friend` folder and start the [LiveSync](https://github.com/zauberzeug/livesync) script:
+ `./sync.py `
+2. this will deploy your local code to the Field Friend
+3. as long as [LiveSync](https://github.com/zauberzeug/livesync) is active, all code change are automatically pushed to the machine
+4. any code changes will automatically trigger a reload on the Field Friend
+
+### Update RoSys and NiceGUI
+
+To utilize personal versions of RoSys and NiceGUI instead of the default ones provided in the docker image,
+modify the `sync.py` file by uncommenting the specific folders.
+
+### Logs
+
+You can see the current log with
+
+```bash
+./docker.sh l rosys
+```
+
+The history of logs can be seen with
+
+```bash
+less -r ~/.rosys/debug.log
+```
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..2c5a1783
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,18 @@
+# About
+
+**Zauberzeug Field Friend** is an autonomous, mobile, and AI-driven agricultural robot developed by [Zauberzeug GmbH](https://zauberzeug.com).
+The robot is specifically designed for autonomous actions, combining lightness, flexibility, and robustness
+to efficiently handle a variety of outdoor tasks.
+Equipped with advanced sensor technologies and camera systems,
+the Field Friend can precisely determine its position, follow crop lines and detect various kinds of plants.
+With it's modular design, the Field Friend can be extended with various tools and sensors to fit the specific needs of the use case.
+
+## Features
+
+- The Open Source software encourages you to modify and enhance the behavior and adapt it to your specific needs.
+- The Modular Design allows equipping with tools from Zauberzeug as well as third-party solutions or your own developments.
+- Advanced Sensing and Autonomy-Algorithms allows autonomous navigation and obstacle avoidance.
+- Full control via web interface remote and locally via WiFi.
+- Manual steering with touch-joystick and keyboard or App.
+- A combined camera/motor calibration for real world coordinate system (unit: meters)
+- ...
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
new file mode 100644
index 00000000..6d595d73
--- /dev/null
+++ b/docs/stylesheets/extra.css
@@ -0,0 +1,22 @@
+:root {
+ --md-primary-fg-color: #6e93d6;
+ --md-default-fg-color: #3a3e42;
+ --md-accent-fg-color: #53b689;
+}
+
+:root > * {
+ --md-code-hl-color: #d8e5fa;
+}
+
+h2.doc-heading {
+ border-bottom: 1pt solid lightgray;
+}
+
+img {
+ width: 70%;
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 5px;
+ box-shadow: rgba(0, 0, 0, 0.25) 0 5px 10px;
+}
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
new file mode 100644
index 00000000..8169f779
--- /dev/null
+++ b/docs/troubleshooting.md
@@ -0,0 +1,18 @@
+# Troubleshooting
+
+## Asyncio Warning
+
+While running RoSys you may see warnings similar to this one:
+
+```
+2021-10-31 15:08:04.040 [WARNING] asyncio: Executing wait_for=<_GatheringFuture pending cb=[()] created at /usr/local/lib/python3.9/asyncio/tasks.py:705> created at /usr/local/lib/python3.9/site-packages/justpy/justpy.py:261> took 0.238 seconds
+```
+
+This means some coroutine is clogging the event loop for too long.
+In the above example it is a whopping 238 ms in which no other actor can do anything.
+This is an eternity when machine communication is expected to happen about every 10 ms.
+The warning also provides a (not so readable) hint where the time is consumed.
+
+The example above is one of the more frequent scenarios.
+It means some code inside a user interaction event handler (e.g. `handle_event()` in `justpy.py`) is blocking.
+Try to figure out which UI event code is responsible by commenting out parts of your logic and try to reproduce the warning systematically.
diff --git a/field_friend/automations/__init__.py b/field_friend/automations/__init__.py
index 0c7e5ccf..e1b92156 100644
--- a/field_friend/automations/__init__.py
+++ b/field_friend/automations/__init__.py
@@ -29,6 +29,7 @@
'Plant',
'Puncher',
'Row',
+ 'KpiProvider',
'find_sequence',
'Weeding',
'BatteryWatcher',
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..2a6bd029
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,56 @@
+site_name: Field Friend Software Documentation
+site_url: https://docs.feldfreund.de/
+nav:
+ - index.md
+ - getting_started.md
+ - Features:
+ - features/user_interface.md # NOTE: shown when clicking on "Examples" folder
+ - features/user_interface.md
+ - features/field_planner.md
+ - Module Reference: reference/
+ - contributing.md
+ - troubleshooting.md
+repo_url: https://github.com/zauberzeug/field_friend
+edit_uri: edit/main/docs/
+theme:
+ name: material
+ font:
+ text: Source Sans Pro
+ features:
+ - content.code.annotate
+extra_css:
+ - stylesheets/extra.css
+markdown_extensions:
+ - toc:
+ permalink: True
+ - admonition
+ - def_list
+ - mdx_include:
+ base_path: docs
+ - pymdownx.highlight
+ - pymdownx.inlinehilite
+ - pymdownx.superfences
+ - pymdownx.snippets
+ - attr_list
+ - footnotes
+plugins:
+ - search
+ - gen-files:
+ scripts:
+ - docs/generate_reference.py
+ - literate-nav:
+ nav_file: SUMMARY.md
+ - section-index
+ - mkdocstrings:
+ default_handler: python
+ handlers:
+ python:
+ options:
+ show_root_heading: true
+ show_root_full_path: false
+ show_source: false
+ show_signature_annotations: true
+ merge_init_into_class: true
+ separate_signature: true
+watch:
+ - field_friend
diff --git a/mkdocs_requirements.txt b/mkdocs_requirements.txt
new file mode 100644
index 00000000..935e46c9
--- /dev/null
+++ b/mkdocs_requirements.txt
@@ -0,0 +1,10 @@
+mkdocs==1.5.3
+mkdocs-material
+mdx-include
+mkdocs-pymdownx-material-extras
+mdx-footnotes2
+mkdocstrings-python
+mkdocs-gen-files
+mkdocs-literate-nav
+mkdocs-section-index
+black
\ No newline at end of file