Skip to content

Commit

Permalink
WIP docs and machine API
Browse files Browse the repository at this point in the history
  • Loading branch information
jantman committed Aug 12, 2024
1 parent 4cdb10c commit f067882
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ repos:
- id: check-toml
- id: check-yaml
# this fails on esphome's !secret notation
exclude: ^esphome-config\.yaml$
exclude: ^esphome-configs/.*$
- id: debug-statements
- id: end-of-file-fixer
- id: trailing-whitespace
Expand Down
25 changes: 24 additions & 1 deletion docs/source/hardware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,27 @@
Hardware
========

TBD.
The "machine control unit" devices use ESP32 microcontrollers; our reference builds use ESP32-DevKitC / ESP32-WROOM-32D boards such as `these from Amazon <https://www.amazon.com/gp/product/B09Z7Q5LKQ/>_. For reasons described in :ref:`the introduction <introduction.mcu-software>` we use `ESPHome <https://esphome.io/>`__ as the software on the ESP32s.

.. _hardware.esphome-configs:

ESPHome Configurations
----------------------

Example ESPHome configurations for various ESPHome versions and various hardware combinations can be found in the `esphome-configs/ directory of the git repo <https://github.com/jantman/machine-access-control/tree/main/esphome-configs>`__ broken down by ESPHome version.

All of the example ESPHome configurations begin with a ``substitutions`` key, which contains a ``machine_name`` substitution. This must be set to the same name as used in the :ref:`configuration.machines-json` config file. If desired, you can override the ``esphome`` ``name`` and ``friendly_name`` values (though this is not recommended).

The ESPHome configurations are based on a `ESPHome secrets.yaml file <https://esphome.io/guides/faq.html#tips-for-using-esphome>`__ for substituting in sensitive values and installation-specific values using the ``!secrets`` substitution operator. The example configurations expect the following secrets to be defined:

ota_password
A password used for OTA updates from ESPHome.

wifi_ssid
WiFi network SSID to connect to.

wifi_password
WiFi network password.

domain_name
Domain name to use for DNS.
6 changes: 6 additions & 0 deletions docs/source/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ powers the RFID-based door access control to the makerspace, dm-mac uses
the `Neon CRM <https://www.neoncrm.com/>`__ as its source for user data,
though that is completely optional and pluggable.

.. _introduction.software:

Software Components
-------------------

At a high level, the system is made up of the central control server and
the ESPHome configuration for the ESP32’s.

.. _introduction.control-server:

Control Server
~~~~~~~~~~~~~~

Expand All @@ -39,6 +43,8 @@ the business logic contained in a central server with relatively “dumb”
machine control units on the machines allows for simpler management of
the system.

.. _introduction.mcu-software:

Machine Control Unit Software
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
substitutions:
machine_name: "MY_HOSTNAME"

esphome:
name: "esp32test"
friendly_name: "esp32test"
name: ${machine_name}
friendly_name: ${machine_name}

esp32:
board: esp32dev
Expand All @@ -11,13 +14,9 @@ esp32:
logger:
level: DEBUG

# Enable Home Assistant API
api:
encryption:
key: !secret api_encryption_key

ota:
password: !secret ota_password
platform: esphome

wifi:
ssid: !secret wifi_ssid
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def docs(session: Session) -> None:
session.install(".")
args = session.posargs or ["-b", "html", "docs/source", "docs/build", "-E", "-W"]

if session.interactive and not session.posargs:
if os.environ.get("DOCS_REBUILD") == "true" and not session.posargs:
args = ["-a", "--watch=docs/source/_static", "--open-browser", *args]

builddir = Path("docs", "build")
Expand Down
11 changes: 7 additions & 4 deletions src/dm_mac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

from flask import Flask

from dm_mac.views.api import api
from dm_mac.views.machine import machineapi


# see: https://github.com/pallets/flask/issues/4786#issuecomment-1416354177
api.register_blueprint(machineapi)


def create_app() -> Flask:
"""Factory to create the app."""
app: Flask = Flask("dm_mac")

from dm_mac.views.api import api

app.register_blueprint(api)

return app
2 changes: 1 addition & 1 deletion src/dm_mac/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
@api.route("/")
def index() -> str:
"""Main API index route - placeholder."""
return "Hello, World!"
return "Nothing to see here..."
11 changes: 7 additions & 4 deletions src/dm_mac/views/machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@
from logging import getLogger
from typing import Any
from typing import Dict
from typing import Tuple
from typing import cast

from flask import Blueprint
from flask import Response
from flask import jsonify
from flask import request


logger: Logger = getLogger(__name__)

machine: Blueprint = Blueprint("machine", __name__, url_prefix="/machine")
machineapi: Blueprint = Blueprint("machine", __name__, url_prefix="/machine")


@machine.route("/update", methods=["POST"])
def update() -> str:
@machineapi.route("/update", methods=["POST"])
def update() -> Tuple[Response, int]:
"""API method to update machine state.
Accepts POSTed JSON containing the following key/value pairs:
Expand All @@ -40,4 +43,4 @@ def update() -> str:
# method for this)
# check if this data would update the state; if not, just call
# noop_update() and return the same display value
return "Not implemented."
return jsonify({"error": "not implemented"}), 501
3 changes: 0 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ def app() -> Generator[Flask, None, None]:
"TESTING": True,
}
)

# other setup can go here

yield app

# clean up / reset resources here


Expand Down
2 changes: 1 addition & 1 deletion tests/views/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ def test_index_response(self, client: FlaskClient) -> None:
"""Test for API index response."""
response: TestResponse = client.get("/api/")
assert response.status_code == 200
assert response.text == "Hello, World!"
assert response.text == "Nothing to see here..."
assert response.headers["Content-Type"] == "text/html; charset=utf-8"
24 changes: 24 additions & 0 deletions tests/views/test_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Tests for /machine API endpoints."""

from flask.testing import FlaskClient
from werkzeug.test import TestResponse


class TestUpdate:
"""Tests for /machine/update API endpoint."""

def test_noop_update_idle_machine(self, client: FlaskClient) -> None:
"""Test for API index response."""
response: TestResponse = client.post("/api/machine/update", json={"foo": "bar"})
assert response.status_code == 501
assert response.json == {"error": "not implemented"}


"""
Questions:
1. Where do I load the machine/user configs? In create_app()?
2. Do I _need_ to use `app.test_client()` via the pytest fixture, or can I just
import create_app() here and call its .test_client() within my test methods?
3. If not, how do I handle setup of the configs unique for each
"""

1 comment on commit f067882

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/dm_mac
   __init__.py80100% 
   cli_utils.py150100% 
   neongetter.py1730100% 
   utils.py170100% 
src/dm_mac/models
   __init__.py00100% 
   machine.py930100% 
   users.py380100% 
src/dm_mac/views
   __init__.py00100% 
   api.py50100% 
   machine.py170100% 
TOTAL3660100% 

Tests Skipped Failures Errors Time
49 0 💤 0 ❌ 0 🔥 3.854s ⏱️

Please sign in to comment.