Skip to content

Commit

Permalink
Merge pull request #245 from somakeit/239-have-a-missing-config-parser
Browse files Browse the repository at this point in the history
239 have a missing config parser
  • Loading branch information
sam57719 authored Oct 23, 2024
2 parents 69e551e + effb5ad commit 9a94732
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 3 deletions.
6 changes: 6 additions & 0 deletions smibhid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Press the space_open or space_closed buttons to call the smib server endpoint ap
- Error information shown on connected displays where configured in modules using ErrorHandler class
- UI Logger captures timestamps of button presses and uploads to SMIB for logging and review of usage patterns
- Space open relay pin optionally sets a GPIO to high or low when the space is open
- Config file checker against config template - useful for upgrades missing config of new features

## Circuit diagram
### Pico W Connections
Expand Down Expand Up @@ -53,6 +54,8 @@ Copy the files from the smibhib folder into the root of a Pico W running Micropy
- Set the space state poll frequency in seconds (>= 5), set to 0 to disable the state poll
- Configure the space open relay pin if required or else set to None, also choose if space open sets pin high or low

If you miss any configuration options, a default will be applied, an error output in the log detailing the configuration item missed including the default value configured and if connected, an error displayed on displays.

## Onboard status LED
The LED on the Pico W board is used to give feedback around network connectivity if you are not able to connect to the terminal output for logs.
* 1 flash at 2 Hz: successful connection
Expand Down Expand Up @@ -101,6 +104,9 @@ Use existing space state buttons, lights, slack API wrapper and watchers as an e
- Enter a new UI state by calling the transition_to() method on a UIstate instance and pass any arguments needed by that state
- You will need to pass any core objects needed by the base UIState class and apply using super() as normal. These are currently HID (for managing the current state instance) and SpaceState so that the open and close buttons are available in all UIs with default space open/closed behaviour.

#### Config template update
If you add a new feature that has configuration options, ensure you set the constant and default value in the config.py file as well as in the config.config_template.py file to allow automated checking of config files to catch upgrade error and misconfigurations.

### UI State diagram
The space state UI state machine is described in this diagram:

Expand Down
2 changes: 1 addition & 1 deletion smibhid/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

## Space state
# Set the space state poll frequency in seconds (>= 5), set to 0 to disable the state poll
space_state_poll_frequency_s = 5
SPACE_STATE_POLL_FREQUENCY_S = 5
# How long to wait for button press to accept extra hours when opening space
ADD_HOURS_INPUT_TIMEOUT = 3

Expand Down
54 changes: 54 additions & 0 deletions smibhid/lib/config/config_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from ulogging import uLogger
import lib.config.config_template as config_template
import config

class ConfigManagement:
"""
Ensure config file contains all items in config_template and that the values are of the correct type.
Apply defaults from config_template where values are missing in config.
"""
def __init__(self) -> None:
self.log = uLogger("ConfigManagement")
self.error_count = 0

def configure_error_handling(self) -> None:
"""
Register errors with the error handler for the configuration management module.
"""
from lib.error_handling import ErrorHandler
self.error_handler = ErrorHandler("ConfigManagement")
self.errors = {
"CFG": "Config errors found, check logs"
}

for error_key, error_message in self.errors.items():
self.error_handler.register_error(error_key, error_message)

return

def check_config(self) -> int:
self.log.info("Checking config file")

for key in dir(config_template):
if not hasattr(config, key):
self.log.error(f"Missing config item in config.py: {key}, setting default value: {getattr(config_template, key)}")
setattr(config, key, getattr(config_template, key))
self.error_count += 1
else:
if not isinstance(getattr(config, key), type(getattr(config_template, key))) and getattr(config_template, key) is not None:
self.log.error(f"Config item in config.py has incorrect type: {key}")
setattr(config, key, getattr(config_template, key))
self.error_count += 1

self.log.info(f"Config file check complete. Errors encountered: {self.error_count}")

if self.error_count > 0:
self.log.error("Please correct the above errors in config.py and restart SMIBHID")
return -1

return 0

def enable_error(self) -> None:
self.error_handler.enable_error("CFG")

return
58 changes: 58 additions & 0 deletions smibhid/lib/config/config_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
## Logging
# Level 0-4: 0 = Disabled, 1 = Critical, 2 = Error, 3 = Warning, 4 = Info
LOG_LEVEL = 2
# Handlers: Populate list with zero or more of the following log output handlers (case sensitive): "Console", "File"
LOG_HANDLERS = ["Console", "File"]
# Max log file size in bytes, there will be a maximum of 2 files at this size created
LOG_FILE_MAX_SIZE = 10240

## IO
SPACE_OPEN_BUTTON = 12
SPACE_CLOSED_BUTTON = 13
SPACE_OPEN_LED = 15
SPACE_CLOSED_LED = 16
# Set to None if no relay/transistor is connected
SPACE_OPEN_RELAY = None
SPACE_OPEN_RELAY_ACTIVE_HIGH = True

## WIFI
WIFI_SSID = ""
WIFI_PASSWORD = ""
WIFI_COUNTRY = "GB"
WIFI_CONNECT_TIMEOUT_SECONDS = 10
WIFI_CONNECT_RETRIES = 1
WIFI_RETRY_BACKOFF_SECONDS = 5
# Leave as none for MAC based unique hostname or specify a custom hostname string
CUSTOM_HOSTNAME = None

NTP_SYNC_INTERVAL_SECONDS = 86400

## Web host
WEBSERVER_HOST = ""
WEBSERVER_PORT = "80"

## Space state
# Set the space state poll frequency in seconds (>= 5), set to 0 to disable the state poll
SPACE_STATE_POLL_FREQUENCY_S = 5
# How long to wait for button press to accept extra hours when opening space
ADD_HOURS_INPUT_TIMEOUT = 3

## I2C
SDA_PIN = 8
SCL_PIN = 9
I2C_ID = 0

## Displays - Populate driver list with connected displays from this supported list: ["LCD1602"]
DISPLAY_DRIVERS = ["LCD1602"]
# Scroll speed for text on displays in characters per second
SCROLL_SPEED = 4

## RFID reader
RFID_ENABLED = False
RFID_SCK = 18
RFID_MOSI = 19
RFID_MISO = 16
RFID_RST = 21
RFID_CS = 17

ENABLE_UI_LOGGING_UPLOAD = False
3 changes: 2 additions & 1 deletion smibhid/lib/error_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ def enable_error(self, key: str):
if key in self.errors:
self.errors[key]['enabled'] = True
self.log.info(f"Enabled error '{key}'")
self.update_errors_on_display()
if hasattr(self, 'display'):
self.update_errors_on_display()
else:
raise ValueError(f"Error key '{key}' not registered.")

Expand Down
2 changes: 1 addition & 1 deletion smibhid/lib/space_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, module_config: ModuleConfig, hid: object) -> None:
self.space_state = None
self.checking_space_state = False
self.checking_space_state_timeout_s = 30
self.space_state_poll_frequency = config.space_state_poll_frequency_s
self.space_state_poll_frequency = config.SPACE_STATE_POLL_FREQUENCY_S
if self.space_state_poll_frequency != 0 and self.space_state_poll_frequency < 5:
self.space_state_poll_frequency = 5
self.state_check_error_open_led_flash_task = None
Expand Down
7 changes: 7 additions & 0 deletions smibhid/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
"""
# Built against Pico W Micropython firmware v1.22.2 https://micropython.org/download/RPI_PICO_W/

from lib.config.config_management import ConfigManagement
config_management = ConfigManagement()
config_errors = config_management.check_config()

from lib.hid import HID

hid = HID()
if config_errors < 0:
config_management.configure_error_handling()
config_management.enable_error()
hid.startup()

0 comments on commit 9a94732

Please sign in to comment.