Skip to content

Commit

Permalink
feat: Improve documentation and auto generate API doc
Browse files Browse the repository at this point in the history
  • Loading branch information
sopelj committed Jun 15, 2024
1 parent 0a08c16 commit a01d0a4
Show file tree
Hide file tree
Showing 13 changed files with 241 additions and 123 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ repos:
- bleak
- bleak-retry-connector
- pytest-asyncio
- mkdocs-gen-files
- repo: https://github.com/adrienverge/yamllint
rev: 'v1.32.0'
hooks:
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
[![License](https://img.shields.io/github/license/sopelj/python-ember-mug.svg)](LICENSE)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen)](https://github.com/pre-commit/pre-commit)

Python Library for interacting with Ember Mugs, Cups and Travel Mugs via Bluetooth
Python Library for interacting with Ember Mugs, Cups, and Travel Mugs via Bluetooth

* Documentation: <https://sopelj.github.io/python-ember-mug>
* GitHub: <https://github.com/sopelj/python-ember-mug>
* PyPI: <https://pypi.org/project/python-ember-mug/>
* [Documentation](https://sopelj.github.io/python-ember-mug)
* [GitHub](https://github.com/sopelj/python-ember-mug)
* [PyPI](https://pypi.org/project/python-ember-mug/)

## Summary

Expand Down
46 changes: 46 additions & 0 deletions docs/gen_api_docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Build a markdown file and summary for all Python files in the module."""
from pathlib import Path

import mkdocs_gen_files


def main() -> None:
"""Generate API documentation for all files."""
nav = mkdocs_gen_files.Nav()

root = Path(__file__).parent.parent
src_root = root / "ember_mug"

for path in sorted(src_root.rglob("*.py")):
module_path = path.relative_to(src_root).with_suffix("")
doc_path = path.relative_to(src_root).with_suffix(".md")
full_doc_path = Path("api", doc_path)
parts = tuple(module_path.parts)

# __init__ should create index
if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")

# Ignore __init__ and __main__ files
elif parts[-1].startswith("__"):
continue

nav_parts = [f"{part}.py" for part in parts]
if not nav_parts:
continue

nav[tuple(nav_parts)] = doc_path.as_posix()

with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
fd.write(f"---\ntitle: {ident}\n---\n\n::: {ident}")

mkdocs_gen_files.set_edit_path(full_doc_path, ".." / path.relative_to(root))

with mkdocs_gen_files.open("api/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())


main()
41 changes: 0 additions & 41 deletions docs/installation.md

This file was deleted.

52 changes: 0 additions & 52 deletions docs/usage.md

This file was deleted.

62 changes: 62 additions & 0 deletions docs/usage/cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
Once this library is installed in your active python environment you can also use the `ember-mug` to interact with your device.
If you clone this project you may also run the commands via `hatch` in the project root. Simply use `hatch run ember-mug` with your desired options

## Command Overview

| Command | Use |
|-------------|-----------------------------------------------------------------------------------|
| `discover` | Find/List all detected unpaired devices in pairing mode |
| `find` | Find *one* already paired devices |
| `info` | Connect to *one* device and print its current state |
| `poll` | Connect to *one* device and print its current state and keep watching for changes |
| `get` | Get the value(s) of one or more attribute(s) by name |
| `set` | Set one or more values on the device |

A few useful common arguments:
- `--mac your:mac:address` (or `-m`) to restrict to that address (useful if you have multiple devices)
- `--raw` (or `-r`) flag to restrict to very basic and parsable output (useful if you want to use the output in a script.)
- `--debug` (or `-d`) flag to enable very verbose output

Some commands have extra options. You can see them by using the `--help` flag after specifying a command. ex.
```bash
ember-mug set --help
```

## Examples

### Find a device in pairing mode (for the first time)
<!-- termynal -->
```bash
$ ember-mug discover
Found Mug: C9:0F:59:D6:33:F9
Name: EMBER MUG
Model: Ember Mug 2 [CM19/CM21M]
Colour: Black
Capacity: 295ml
```

### Find a previously paired device
<!-- termynal -->
```bash
$ ember-mug discover
Found device: C9:0F:59:D6:33:F9: Ember Ceramic Mug
```

### Fetch info and keep listening for changes
<!-- termynal -->
```bash
$ ember-mug poll
Found mug: C9:0F:59:D6:33:F9: Ember Ceramic Mug
```

### Get the value(s) of specific attribute(s)
<!-- termynal -->
```bash
$ ember-mug get name target-temp
```

### Set the value(s) of specific attribute(s)
<!-- termynal -->
```bash
$ ember-mug set --name "My mug" --target-temp 56.8
```
36 changes: 36 additions & 0 deletions docs/usage/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Installation

To install Python Ember Mug, run this command in your terminal:

``` console
pip install python-ember-mug
```

This is the preferred method to install Python Ember Mug, as it will always install the most recent stable release.

If you don't have `pip` installed, this [Python installation guide](https://pip.pypa.io/en/stable/installation/) can guide you through the process.

## From source

The source for Python Ember Mug can be downloaded from the [GitHub repo](https://github.com/sopelj/python-ember-mug).

You can either clone the public repository:

``` console
git clone git://github.com/sopelj/python-ember-mug
```

Or download the [tarball](https://github.com/sopelj/python-ember-mug/tarball/main):

``` console
curl -OJL https://github.com/sopelj/python-ember-mug/tarball/main
```

Once you have a copy of the source, you can install it with:

``` console
pip install .
```

[pip]: https://pip.pypa.io
[Python installation guide]: http://docs.python-guide.org/en/latest/starting/installation/
21 changes: 21 additions & 0 deletions docs/usage/python.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
```python
from ember_mug.scanner import find_mug, discover_mugs
from ember_mug.mug import EmberMug

# if first time with mug in pairing
mugs = await discover_mugs()
device = mugs[0]
# after paired you can simply use
device = await find_mug()
mug = EmberMug(device)
await mug.update_all()
print(mug.data.formatted)
await mug.disconnect()

# You can also use connection as a context manager
# if you want to ensure connection before starting and cleanup on exit
async with mug.connection():
print('Connected.\nFetching Info')
await mug.update_all()
print(mug.data.formatted)
```
14 changes: 7 additions & 7 deletions ember_mug/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ async def find_device(args: Namespace) -> tuple[BLEDevice, AdvertisementData]:
try:
device, advertisement = await find_mug(mac=args.mac, adapter=args.adapter)
except BleakError as e:
print(f"An error occurred trying to find a mug: {e}")
print(f"An error occurred trying to find a device: {e}")
sys.exit(1)
if not device or not advertisement:
print("No mug was found.")
print("No device was found.")
sys.exit(1)
if not args.raw:
print("Found mug:", device)
print("Found device:", device)
return device, advertisement


Expand All @@ -63,10 +63,10 @@ async def discover(args: Namespace) -> list[tuple[BLEDevice, AdvertisementData]]
try:
mugs = await discover_mugs(mac=args.mac)
except BleakError as e:
print(f"An error occurred trying to discover mugs: {e}")
print(f"An error occurred trying to discover devices: {e}")
sys.exit(1)
if not mugs:
print('No mugs were found. Be sure it is in pairing mode. Or use "find" if already paired.')
print('No devices were found. Be sure it is in pairing mode. Or use "find" if already paired.')
sys.exit(1)

for mug, advertisement in mugs:
Expand All @@ -75,7 +75,7 @@ async def discover(args: Namespace) -> list[tuple[BLEDevice, AdvertisementData]]
else:
model_info = get_model_info_from_advertiser_data(advertisement)
model_number = model_info.model.value if model_info.model else "Unknown Model"
print("Found mug:", mug)
print(f"Found {model_info.device_type.value}:", mug)
print("Name:", advertisement.local_name)
print("Model:", f"{model_info.name} [{model_number}]")
print("Colour:", model_info.colour.value if model_info.colour else "Unknown")
Expand Down Expand Up @@ -228,7 +228,7 @@ def __init__(self) -> None:
info_parsers.add_argument("-e", "--extra", help="Show extra info", action="store_true")
info_parsers.add_argument("--imperial", help="Use Imperial units", action="store_true")
subparsers.add_parser("info", description="Fetch all info from device", parents=[shared_parser, info_parsers])
subparsers.add_parser("poll", description="Poll mug for information", parents=[shared_parser, info_parsers])
subparsers.add_parser("poll", description="Poll device for information", parents=[shared_parser, info_parsers])
get_parser = subparsers.add_parser("get", description="Get mug value", parents=[shared_parser, info_parsers])
get_parser.add_argument(dest="attributes", metavar="ATTRIBUTE", choices=get_attribute_names, nargs="+")
set_parser = subparsers.add_parser("set", description="Set mug value", parents=[shared_parser, info_parsers])
Expand Down
4 changes: 3 additions & 1 deletion ember_mug/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ async def find_mug(
with contextlib.suppress(asyncio.TimeoutError):
async with async_timeout.timeout(timeout):
async for device, advertisement in scanner.advertisement_data():
if (not mac and device.name.startswith("Ember")) or (mac and device.address.lower() == mac):
if (not mac and device.name and device.name.startswith("Ember")) or (
mac and device.address.lower() == mac
):
return device, advertisement
return None, None
Loading

0 comments on commit a01d0a4

Please sign in to comment.