machine_access_control 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dm_mac/__init__.py ADDED
@@ -0,0 +1,14 @@
1
+ """Decatur Makers Machine Access Control."""
2
+
3
+ from flask import Flask
4
+
5
+
6
+ def create_app() -> Flask:
7
+ """Factory to create the app."""
8
+ app: Flask = Flask("dm_mac")
9
+
10
+ from dm_mac.views.api import api
11
+
12
+ app.register_blueprint(api)
13
+
14
+ return app
@@ -0,0 +1,73 @@
1
+ """Classes and functions related to machine state."""
2
+ from typing import Optional
3
+ from logging import getLogger, Logger
4
+ from time import time
5
+
6
+
7
+ logger: Logger = getLogger(__name__)
8
+
9
+ DEFAULT_DISPLAY_TEXT: str = 'Please Insert\nRFID Card'
10
+
11
+
12
+ class MachineState:
13
+ """Object representing frozen state in time of a machine."""
14
+
15
+ def __init__(self, machine_name: str):
16
+ """Initialize a new MachineState instance."""
17
+ logger.debug('Instantiating new MachineState for %s', machine_name)
18
+ #: The name of the machine
19
+ self.name: str = machine_name
20
+ #: Float timestamp of the machine's last checkin time
21
+ self.last_checkin: Optional[float] = None
22
+ #: Float timestamp of the last time that machine state changed,
23
+ #: not counting `current_amps` or timestamps.
24
+ self.last_update: Optional[float] = None
25
+ #: Value of the RFID card/fob in use, or None if not present.
26
+ self.rfid_value: Optional[str] = None
27
+ #: Float timestamp when `rfid_value` last changed to a non-None value.
28
+ self.rfid_present_since: Optional[float] = None
29
+ #: Whether the output relay is on or not.
30
+ self.relay_is_on: bool = False
31
+ #: Whether the output relay should be on or not.
32
+ self.relay_desired_state: bool = False
33
+ #: Whether the machine's Oops button has been pressed.
34
+ self.is_oopsed: bool = False
35
+ #: Whether the machine is locked out from use.
36
+ self.is_locked_out: bool = False
37
+ #: Whether the machine is force-enabled without RFID present.
38
+ self.is_force_enabled: bool = False
39
+ #: Last reported output ammeter reading (if equipped).
40
+ self.current_amps: float = 0
41
+ #: Text currently displayed on the machine LCD screen
42
+ self.display_text: str = DEFAULT_DISPLAY_TEXT
43
+ self._load_from_cache()
44
+
45
+ def _save_cache(self):
46
+ """Save machine state cache to disk."""
47
+ raise NotImplementedError()
48
+
49
+ def update_has_changes(
50
+ self, rfid_value: str, relay_state: bool, oops: bool, amps: float
51
+ ) -> bool:
52
+ """Return whether or not the update causes changes to significant state values."""
53
+ if (
54
+ rfid_value != self.rfid_value or
55
+ relay_state != self.relay_is_on or
56
+ oops != self.is_oopsed
57
+ ):
58
+ return True
59
+ return False
60
+
61
+ def noop_update(self, amps: float):
62
+ """Just update amps and last_checkin and save cache."""
63
+ self.current_amps = amps
64
+ self.last_checkin = time()
65
+ self._save_cache()
66
+
67
+ @property
68
+ def machine_response(self) -> dict:
69
+ """Return the response dict to send to the machine."""
70
+ return {
71
+ 'relay': self.relay_desired_state,
72
+ 'display': self.display_text
73
+ }
dm_mac/py.typed ADDED
File without changes
@@ -0,0 +1 @@
1
+ """Init for views (empty)."""
dm_mac/views/api.py ADDED
@@ -0,0 +1,12 @@
1
+ """API Views."""
2
+
3
+ from flask import Blueprint
4
+
5
+
6
+ api: Blueprint = Blueprint("api", __name__, url_prefix="/api")
7
+
8
+
9
+ @api.route("/")
10
+ def index():
11
+ """Main API index route - placeholder."""
12
+ return "Hello, World!"
@@ -0,0 +1,30 @@
1
+ """Views related to machine endpoints."""
2
+ from typing import Any, Dict
3
+ from flask import Blueprint, request, app
4
+
5
+
6
+ machine: Blueprint = Blueprint("machine", __name__, url_prefix="/machine")
7
+
8
+
9
+ @machine.route("/update", methods=['POST'])
10
+ def update():
11
+ """
12
+ API method to update machine state.
13
+
14
+ Accepts POSTed JSON containing the following key/value pairs:
15
+
16
+ - "name" (string) - name of the machine sending the update
17
+ - "rfid_value" (string) - value of the RFID fob/card that is currently
18
+ present in the machine, or empty string if none present
19
+ - "relay_state" (boolean) - the current on/off (true/false) state of the
20
+ relay
21
+ - "oops" (boolean) - whether the oops button is pressed, or has been pressed
22
+ since the last check-in
23
+ - "amps" (float) - amperage value from the current clamp ammeter, if present,
24
+ or 0.0 otherwise.
25
+ """
26
+ data: Dict[str, Any] = request.json
27
+ machine_name: str = data.pop('name')
28
+ # get the MachineState object for this machine, or else return an error
29
+ # that error should be formatted for display on the device (helper method for this)
30
+ # check if this data would update the state; if not, just call noop_update() and return the same display value
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jason Antman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: machine_access_control
3
+ Version: 0.1.0
4
+ Summary: Decatur Makers Machine Access Control package
5
+ Home-page: https://github.com/jantman/machine_access_control
6
+ License: MIT
7
+ Author: Jason Antman
8
+ Author-email: jason@jasonantman.com
9
+ Requires-Python: >=3.12,<4.0
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: flask (>=3.0.3,<4.0.0)
15
+ Project-URL: Changelog, https://github.com/jantman/machine_access_control/releases
16
+ Project-URL: Documentation, https://github.com/jantman/machine_access_control
17
+ Project-URL: Repository, https://github.com/jantman/machine_access_control
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Decatur Makers Machine Access Control (dm-mac)
21
+
22
+ [![Project Status: Concept – Minimal or no implementation has been done yet, or the repository is only intended to be a limited example, demo, or proof-of-concept.](https://www.repostatus.org/badges/latest/concept.svg)](https://www.repostatus.org/#concept)
23
+ [![Tests](https://github.com/jantman/machine_access_control/workflows/Tests/badge.svg)](https://github.com/jantman/machine_access_control/actions?workflow=Tests)
24
+
25
+ This is a software and hardware project for using RFID cards/fobs to control use of various power tools and equipment in the [Decatur Makers](https://decaturmakers.org/) makerspace. It is made up of custom ESP32-based hardware (machine control units) controlling power to each enabled machine and running ESPHome, and a central access control/management/logging server application written in Python/Flask. Like our [“glue” server](https://github.com/decaturmakers/glue) that powers the RFID-based door access control to the makerspace, dm-mac uses the [Neon CRM](https://www.neoncrm.com/) (or a local flat-file when in development mode) as its backend datastore.
26
+
27
+ ## Software Components
28
+
29
+ At a high level, the system is made up of the central control server and the ESPHome configuration for the ESP32’s.
30
+
31
+ ### Control Server
32
+
33
+ This is a Python/Flask application that provides authentication and authorization for users via RFID credentials, control of the ESP32-based machine control units;, and logging and monitoring as well as basic management capabilities.
34
+
35
+ **Why not use the Glue server?** First, because the glue server is currently running in a cloud hosting provider. That makes sense for its purpose, but less so for direct control of physical machines in our space. We want the machine access control system to always function, regardless of the state of our Internet connection, with low latency. We also aren’t concerned about reliability through a power outage, as that will also prevent the controlled machines from working. Secondly, having the business logic contained in a central server with relatively “dumb” machine control units on the machines allows for simpler management of the system.
36
+
37
+ ### Machine Control Unit Software
38
+
39
+ The machine control units run ESPHome, because it is well-supported with an active community, requires minimal programming (just a YAML configuration), and allows updating and managing many devices wirelessly from a central point. The machine control units (and their ESPHome configuration) are relatively simple - they just react to events (RFID card insertion or removal, button press, or a timer ticking), send their current state to the control server via a HTTP POST, and receive a response with the intended state of their outputs (control relay, LCD screen, LEDs). All of the logic of the system is contained in the central control server.
40
+
41
+ In the event of an extended control server outage, special event, or other exigent circumstance, the machine control unit software is configured with a list of permanently-authorized RFID cards that will enable the machine without requiring authorization from the control server.
42
+
43
+ ## Installation
44
+
45
+ It's recommended to install and run via Docker. Details TBD.
46
+
47
+ ## Contributing and Development
48
+
49
+ Contributions are very welcome. To learn more, see the [Contributor Guide](https://github.com/jantman/machine_access_control/blob/main/CONTRIBUTING.md).
50
+
51
+ ## License
52
+
53
+ Distributed under the terms of the [MIT license](https://github.com/jantman/machine_access_control/blob/main/LICENSE), _Machine_Access_Control_ (`dm_mac`) is free and open source software.
54
+
@@ -0,0 +1,10 @@
1
+ dm_mac/__init__.py,sha256=_uuRQaFFpWBEakxBpf-SjspfrT7arO1YWQOWGSOO1cU,256
2
+ dm_mac/machine_state.py,sha256=yzIGjqebHa_M-fg4rOff9MggccO91LgsIZUFOZ1jRJI,2810
3
+ dm_mac/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ dm_mac/views/__init__.py,sha256=U79DC2wZ9YQSJJfdwuS5Ben45AIvO-oPHpinq39KNwQ,30
5
+ dm_mac/views/api.py,sha256=InNokuWIV_C7GxsNNN2MkFL_yTpzkR8ouvaQytYpCUA,215
6
+ dm_mac/views/machine.py,sha256=mMUcc1l4kEhKbTC73A7hM3pik9TGThw46wYvigDBSmA,1251
7
+ machine_access_control-0.1.0.dist-info/LICENSE,sha256=TeKo6ThhJh4LJ9xJziWpGNKpBA2Ojnweppy0rzaRbDw,1069
8
+ machine_access_control-0.1.0.dist-info/METADATA,sha256=lN0V-osN4I588WZTPGEu9Q6EH3Ne7Kxnvha2LgbhmQw,4416
9
+ machine_access_control-0.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
10
+ machine_access_control-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 1.9.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any