Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
"type": "Soil Moisture",
"units": "NoUnits",
"min": 0,
"max": 1023
"max": 1023,
"azure": {
"device_id": "soil_moisture_sensor"
}
},
{
"type": "Temperature",
"units": "Celsius",
"min": -20,
"max": 45
"max": 45,
"azure": {
"device_id": "temperature_sensor"
}
}
]
109 changes: 0 additions & 109 deletions soil-moisture-sensor/app.py

This file was deleted.

61 changes: 61 additions & 0 deletions soil_moisture_sensor/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import json
import os
import time
from types import SimpleNamespace

from counterfit_connection import CounterFitConnection
from iot.azure_device import IoTDevice
from iot.azure_iot_hub import IoTHubClient
from typing import Any


def initialise_device(sensor: Any, connection_str: str, i: int) -> IoTDevice:
iot_hub = IoTHubClient(
sensor.type,
connection_str,
)

iot_device = IoTDevice(
sensor.type,
sensor.units,
sensor.min,
sensor.max,
sensor.azure.device_id,
iot_hub,
)

iot_device.create_sensor(i)

iot_device.configure_sensor(i)

return iot_device


def run() -> None:
iot_devices = []

CounterFitConnection.init("127.0.0.1", 5000)

conf_file = open("./config.json", "r")
conf = json.load(conf_file, object_hook=lambda d: SimpleNamespace(**d))

for i, sensor in enumerate(conf):
conn_str_env_var = "{}_connection_str".format(sensor.azure.device_id)
connection_string = os.getenv(conn_str_env_var)

if connection_string == "":
raise Exception(
"no connection string found with the name: " + conn_str_env_var
)

iot_device = initialise_device(sensor, connection_string, i)
iot_devices.append(iot_device)

while True:
for i, device in enumerate(iot_devices):
device.read_sensor_values(i)
time.sleep(10)


if __name__ == "__main__":
run()
Empty file.
82 changes: 82 additions & 0 deletions soil_moisture_sensor/iot/azure_device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import json
import time

from azure.iot.device import Message

from .azure_iot_hub import IoTHubClient


class IoTDevice:
ALLOWED_SENSORS = ["Soil Moisture", "Temperature"]

def __init__(
self,
type: str,
units: str,
min: int,
max: int,
device_id: str,
client: IoTHubClient,
) -> None:
self.type = type
self.units = units
self.min = min
self.max = max
self.device_id = device_id
self.client = client

def create_sensor(self, i: int) -> None:
if self.type not in IoTDevice.ALLOWED_SENSORS:
raise Exception("sensor is not valid")

payload = {
"type": self.type,
"pin": i,
"i2c_pin": i,
"port": "/dev/ttyAMA0",
"name": "sensor_" + str(i),
"unit": self.units,
"i2c_unit": self.units,
}

http_code = self.client.post("/create_sensor", payload)
if http_code != 200:
raise Exception(
"unsuccessful request to counterfit with payload: " + payload
)

def configure_sensor(self, i: int) -> None:
if (self.min or self.max) is None:
raise Exception("no min or max value set for sensor")

payload = {
"port": str(i),
"value": i,
"is_random": True,
"random_min": self.min,
"random_max": self.max,
}

http_code = self.client.post("/integer_sensor_settings", payload)
if http_code != 200:
raise Exception(
"unsuccessful request to counterfit with payload: " + payload
)

def read_sensor_values(self, i):
sensor_dict = {}

sensor_name = "{}_{}".format(self.type, i)
sensor_dict[sensor_name] = self.client.adc.read(i)
print(sensor_name + " " + str(sensor_dict[sensor_name]))

json_sensor_name = self.type.lower().replace(" ", "_")

message = Message(
json.dumps(
{
json_sensor_name: sensor_dict[sensor_name],
}
)
)
self.client.device_client.send_message(message)
44 changes: 44 additions & 0 deletions soil_moisture_sensor/iot/azure_iot_hub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
import json
import requests
from azure.iot.device import IoTHubDeviceClient, MethodResponse
from counterfit_shims_grove.adc import ADC


class IoTHubClient:
def __init__(
self,
device_name: str,
connection_str: str,
) -> None:
self.device_name = device_name
self.adc = ADC()
self.connection_str = connection_str
try:
self.device_client = self.create_device_client()
except Exception as e:
print("couldn't create device_client")
print(e)
os._exit(502)

self.device_client.connect()
self.device_client.on_method_request_received = self.set_request_handler
print("connected to " + device_name)

def set_request_handler(self, request):
print("Direct method received - ", request.name)

method_response = MethodResponse.create_from_method_request(request, 200)
self.device_client.send_method_response(method_response)

def create_device_client(self) -> IoTHubDeviceClient:
return IoTHubDeviceClient.create_from_connection_string(self.connection_str)

def post(self, endpoint, payload) -> int:
url = "http://localhost:5000{}".format(endpoint)

payload = json.dumps(payload)
headers = {"Content-Type": "application/json"}
response = requests.request("POST", url, headers=headers, data=payload)

return response.status_code
15 changes: 15 additions & 0 deletions soil_moisture_sensor/test_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from app import create_sensor
import logging
import pytest


def test_create_sensor(mocker):
test_cases = [
{"name": "valid sensor is created", "input": [1, "Soil Mositure"], "want": True}
]
for test_case in test_cases:
logging.info(test_case["name"])
something = mocker.patch("app._do_counterfit_req", return_value=200)
something.assert_once_called()
got = create_sensor(test_case["input"])
assert got == test_case["want"]