From 9ffc7edef7fc9f33fdc969081ffedd4af76b43bf Mon Sep 17 00:00:00 2001
From: Ellis Percival <flyte@failcode.co.uk>
Date: Tue, 7 Nov 2017 16:21:51 +0000
Subject: [PATCH] Add optional initial values for pins and make module cleanup
 optional. Fixes #23

---
 config.example.yml                  |   2 +
 config.schema.yml                   |  12 ++-
 pi_mqtt_gpio/__init__.py            | 154 +++++++++++++++++++++++++++-
 pi_mqtt_gpio/modules/__init__.py    |   5 +
 pi_mqtt_gpio/modules/pcf8574.py     |   6 ++
 pi_mqtt_gpio/modules/raspberrypi.py |   7 +-
 pi_mqtt_gpio/modules/stdio.py       |   6 ++
 pi_mqtt_gpio/server.py              |   5 +
 8 files changed, 191 insertions(+), 6 deletions(-)

diff --git a/config.example.yml b/config.example.yml
index bc50de4..b3cd54c 100644
--- a/config.example.yml
+++ b/config.example.yml
@@ -16,6 +16,7 @@ gpio_modules:
 
   - name: dev
     module: stdio
+    cleanup: no
 
 digital_inputs:
   - name: button
@@ -32,6 +33,7 @@ digital_outputs:
     pin: 20
     on_payload: "ON"
     off_payload: "OFF"
+    initial: low
 
   - name: test
     module: dev
diff --git a/config.schema.yml b/config.schema.yml
index 5ebb7f1..0fd7d4e 100644
--- a/config.schema.yml
+++ b/config.schema.yml
@@ -67,6 +67,10 @@ gpio_modules:
         type: string
         required: yes
         empty: no
+      cleanup:
+        type: boolean
+        required: no
+        default: yes
 
 digital_inputs:
   type: list
@@ -132,4 +136,10 @@ digital_outputs:
       inverted:
         type: boolean
         required: no
-        default: no
\ No newline at end of file
+        default: no
+      initial:
+        type: string
+        required: no
+        allowed:
+          - high
+          - low
diff --git a/pi_mqtt_gpio/__init__.py b/pi_mqtt_gpio/__init__.py
index 329e6d8..1a5d53e 100644
--- a/pi_mqtt_gpio/__init__.py
+++ b/pi_mqtt_gpio/__init__.py
@@ -1,4 +1,150 @@
-import sys
-print("FATAL ERROR: The file at pi_mqtt_gpio/__init__.py should be replaced us"
-      "ing 'make schema' before packaging.")
-sys.exit(1)
+import yaml
+
+CONFIG_SCHEMA = yaml.load("""
+mqtt:
+  type: dict
+  required: yes
+  schema:
+    host:
+      type: string
+      empty: no
+      required: no
+      default: localhost
+    port:
+      type: integer
+      min: 1
+      max: 65535
+      required: no
+      default: 1883
+    user:
+      type: string
+      required: no
+      default: ""
+    password:
+      type: string
+      required: no
+      default: ""
+    topic_prefix:
+      type: string
+      required: no
+      default: ""
+      coerce: rstrip_slash
+    protocol:
+      type: string
+      required: no
+      empty: no
+      coerce: tostring
+      default: "3.1.1"
+      allowed:
+        - "3.1"
+        - "3.1.1"
+    status_topic:
+      type: string
+      required: no
+      default: status
+    status_payload_running:
+      type: string
+      required: no
+      default: running
+    status_payload_stopped:
+      type: string
+      required: no
+      default: stopped
+    status_payload_dead:
+      type: string
+      required: no
+      default: dead
+
+gpio_modules:
+  type: list
+  required: yes
+  schema:
+    type: dict
+    allow_unknown: yes
+    schema:
+      name:
+        type: string
+        required: yes
+        empty: no
+      module:
+        type: string
+        required: yes
+        empty: no
+      cleanup:
+        type: boolean
+        required: no
+        default: yes
+
+digital_inputs:
+  type: list
+  required: no
+  default: []
+  schema:
+    type: dict
+    schema:
+      name:
+        type: string
+        required: yes
+        empty: no
+      module:
+        type: string
+        required: yes
+        empty: no
+      pin:
+        type: integer
+        required: yes
+        min: 0
+      on_payload:
+        type: string
+        required: yes
+        empty: no
+      off_payload:
+        type: string
+        required: yes
+        empty: no
+      pullup:
+        type: boolean
+        required: no
+        default: no
+      pulldown:
+        type: boolean
+        required: no
+        default: no
+
+digital_outputs:
+  type: list
+  required: no
+  default: []
+  schema:
+    type: dict
+    schema:
+      name:
+        type: string
+        required: yes
+      module:
+        type: string
+        required: yes
+      pin:
+        type: integer
+        required: yes
+        min: 0
+      on_payload:
+        type: string
+        required: no
+        empty: no
+      off_payload:
+        type: string
+        required: no
+        empty: no
+      inverted:
+        type: boolean
+        required: no
+        default: no
+      initial:
+        type: string
+        required: no
+        allowed:
+          - high
+          - low
+
+""")
diff --git a/pi_mqtt_gpio/modules/__init__.py b/pi_mqtt_gpio/modules/__init__.py
index 2e82893..7b7002c 100644
--- a/pi_mqtt_gpio/modules/__init__.py
+++ b/pi_mqtt_gpio/modules/__init__.py
@@ -11,6 +11,11 @@ BASE_SCHEMA = {
     "module": {
         "required": True,
         "empty": False
+    },
+    "cleanup": {
+        "required": False,
+        "type": "boolean",
+        "default": True
     }
 }
 
diff --git a/pi_mqtt_gpio/modules/pcf8574.py b/pi_mqtt_gpio/modules/pcf8574.py
index 1ca05f8..00ddb28 100644
--- a/pi_mqtt_gpio/modules/pcf8574.py
+++ b/pi_mqtt_gpio/modules/pcf8574.py
@@ -36,6 +36,12 @@ class GPIO(GenericGPIO):
     def setup_pin(self, pin, direction, pullup, pin_config):
         if direction == PinDirection.INPUT and pullup is not None:
             self.io.port[pin] = PULLUPS[pullup]
+        initial = pin_config.get("initial")
+        if initial is not None:
+            if initial == 'high':
+                self.set_pin(pin, True)
+            elif initial == 'low':
+                self.set_pin(pin, False)
 
     def set_pin(self, pin, value):
         self.io.port[pin] = value
diff --git a/pi_mqtt_gpio/modules/raspberrypi.py b/pi_mqtt_gpio/modules/raspberrypi.py
index 8f505c9..a693095 100644
--- a/pi_mqtt_gpio/modules/raspberrypi.py
+++ b/pi_mqtt_gpio/modules/raspberrypi.py
@@ -36,7 +36,12 @@ class GPIO(GenericGPIO):
         else:
             pullup = PULLUPS[pullup]
 
-        self.io.setup(pin, direction, pull_up_down=pullup)
+        initial = {
+            None: -1,
+            "low": 0,
+            "high": 1
+        }[pin_config.get("initial")]
+        self.io.setup(pin, direction, pull_up_down=pullup, initial=initial)
 
     def set_pin(self, pin, value):
         self.io.output(pin, value)
diff --git a/pi_mqtt_gpio/modules/stdio.py b/pi_mqtt_gpio/modules/stdio.py
index 5a55849..8aa68fa 100644
--- a/pi_mqtt_gpio/modules/stdio.py
+++ b/pi_mqtt_gpio/modules/stdio.py
@@ -13,6 +13,12 @@ class GPIO(GenericGPIO):
     def setup_pin(self, pin, direction, pullup, pin_config):
         print("setup_pin(pin=%r, direction=%r, pullup=%r, pin_config=%r)" % (
             pin, direction, pullup, pin_config))
+        initial = pin_config.get("initial")
+        if initial is not None:
+            if initial == "high":
+                self.set_pin(pin, True)
+            elif initial == "low":
+                self.set_pin(pin, False)
 
     def set_pin(self, pin, value):
         print("set_pin(pin=%r, value=%r)" % (pin, value))
diff --git a/pi_mqtt_gpio/server.py b/pi_mqtt_gpio/server.py
index 0caed5f..ab7818e 100644
--- a/pi_mqtt_gpio/server.py
+++ b/pi_mqtt_gpio/server.py
@@ -23,6 +23,7 @@ LOG_LEVEL_MAP = {
 }
 RECONNECT_DELAY_SECS = 5
 GPIO_MODULES = {}
+GPIO_CONFIGS = {}
 LAST_STATES = {}
 SET_TOPIC = "set"
 SET_ON_MS_TOPIC = "set_on_ms"
@@ -431,6 +432,7 @@ if __name__ == "__main__":
     client = init_mqtt(config["mqtt"], config["digital_outputs"])
 
     for gpio_config in config["gpio_modules"]:
+        GPIO_CONFIGS[gpio_config["name"]] = gpio_config
         try:
             GPIO_MODULES[gpio_config["name"]] = configure_gpio_module(
                 gpio_config)
@@ -491,6 +493,9 @@ if __name__ == "__main__":
         # This should also quit the mqtt loop thread.
         client.disconnect()
         for name, gpio in GPIO_MODULES.items():
+            if not GPIO_CONFIGS[name]["cleanup"]:
+                _LOG.info("Cleanup disabled for module %r.", name)
+                continue
             try:
                 gpio.cleanup()
             except Exception:
-- 
GitLab