[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.236.0/containers/python-3/.devcontainer/base.Dockerfile\n\n# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster\nARG VARIANT=\"3.10-bullseye\"\nFROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}\n\n# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10\nARG NODE_VERSION=\"none\"\nRUN if [ \"${NODE_VERSION}\" != \"none\" ]; then su vscode -c \"umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1\"; fi\n\n# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.\n# COPY requirements.txt /tmp/pip-tmp/\n# RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \\\n#    && rm -rf /tmp/pip-tmp\n\n# [Optional] Uncomment this section to install additional OS packages.\n# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \\\n#     && apt-get -y install --no-install-recommends <your-package-list-here>\n\n# [Optional] Uncomment this line to install global node packages.\n# RUN su vscode -c \"source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>\" 2>&1"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:\n// https://github.com/microsoft/vscode-dev-containers/tree/v0.236.0/containers/python-3\n{\n\t\"name\": \"Python 3\",\n\t\"build\": {\n\t\t\"dockerfile\": \"Dockerfile\",\n\t\t\"context\": \"..\",\n\t\t\"args\": { \n\t\t\t// Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6\n\t\t\t// Append -bullseye or -buster to pin to an OS version.\n\t\t\t// Use -bullseye variants on local on arm64/Apple Silicon.\n\t\t\t\"VARIANT\": \"3.10-bullseye\",\n\t\t\t// Options\n\t\t\t\"NODE_VERSION\": \"none\"\n\t\t}\n\t},\n\n\t// Configure tool-specific properties.\n\t\"customizations\": {\n\t\t// Configure properties specific to VS Code.\n\t\t\"vscode\": {\n\t\t\t// Set *default* container specific settings.json values on container create.\n\t\t\t\"settings\": { \n\t\t\t\t\"python.defaultInterpreterPath\": \"/usr/local/bin/python\",\n\t\t\t\t\"python.linting.enabled\": true,\n\t\t\t\t\"python.linting.pylintEnabled\": true,\n\t\t\t\t\"python.formatting.autopep8Path\": \"/usr/local/py-utils/bin/autopep8\",\n\t\t\t\t\"python.formatting.blackPath\": \"/usr/local/py-utils/bin/black\",\n\t\t\t\t\"python.formatting.yapfPath\": \"/usr/local/py-utils/bin/yapf\",\n\t\t\t\t\"python.linting.banditPath\": \"/usr/local/py-utils/bin/bandit\",\n\t\t\t\t\"python.linting.flake8Path\": \"/usr/local/py-utils/bin/flake8\",\n\t\t\t\t\"python.linting.mypyPath\": \"/usr/local/py-utils/bin/mypy\",\n\t\t\t\t\"python.linting.pycodestylePath\": \"/usr/local/py-utils/bin/pycodestyle\",\n\t\t\t\t\"python.linting.pydocstylePath\": \"/usr/local/py-utils/bin/pydocstyle\",\n\t\t\t\t\"python.linting.pylintPath\": \"/usr/local/py-utils/bin/pylint\"\n\t\t\t},\n\t\t\t\n\t\t\t// Add the IDs of extensions you want installed when the container is created.\n\t\t\t\"extensions\": [\n\t\t\t\t\"ms-python.python\",\n\t\t\t\t\"ms-python.vscode-pylance\",\n\t\t\t\t\"github.copilot\"\n\t\t\t]\n\t\t}\n\t},\n\n\t// Use 'forwardPorts' to make a list of ports inside the container available locally.\n\t// \"forwardPorts\": [],\n\n\t// Use 'postCreateCommand' to run commands after the container is created.\n\t\"postCreateCommand\": \"pip3 install --user -r requirements.txt\",\n\n\t// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.\n\t\"remoteUser\": \"vscode\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n\n.vscode\n.DS_Store\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Jim Bennett\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# CounterFit\n\n[![GitHub license](https://img.shields.io/github/license/CounterFit-IoT/CounterFit.svg)](https://github.com/CounterFit-IoT/CounterFit/blob/master/LICENSE)\n[![GitHub contributors](https://img.shields.io/github/contributors/CounterFit-IoT/CounterFit.svg)](https://GitHub.com/CounterFit-IoT/CounterFit/graphs/contributors/)\n[![GitHub issues](https://img.shields.io/github/issues/CounterFit-IoT/CounterFit.svg)](https://GitHub.com/CounterFit-IoT/CounterFit/issues/)\n[![GitHub pull-requests](https://img.shields.io/github/issues-pr/CounterFit-IoT/CounterFit.svg)](https://GitHub.com/CounterFit-IoT/CounterFit/pull/)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\n[![GitHub watchers](https://img.shields.io/github/watchers/CounterFit-IoT/CounterFit.svg?style=social&label=Watch&maxAge=2592000)](https://GitHub.com/CounterFit-IoT/CounterFit/watchers/)\n[![GitHub forks](https://img.shields.io/github/forks/CounterFit-IoT/CounterFit.svg?style=social&label=Fork&maxAge=2592000)](https://GitHub.com/CounterFit-IoT/CounterFit/network/)\n[![GitHub stars](https://img.shields.io/github/stars/CounterFit-IoT/CounterFit.svg?style=social&label=Star&maxAge=2592000)](https://GitHub.com/CounterFit-IoT/CounterFit/stargazers/)\n\n![The CounterFit logo](./images/CounterFitLogo.png)\n\nIoT is great fun, but has a downside - hardware. You need access to a range of devices such as sensors and actuators to build your IoT projects. Sometimes you might have these devices, other times you may not - maybe you are waiting for a delivery, or parts are out of stock, or they are too expensive.\n\nThat's where this tool comes in.\n\n## What is CounterFit\n\nCounterFit is a tool that is designed to fake various IoT hardware components, such as LEDs, buttons, temperature sensors and the like, that you can then access from IoT device code running on your computer rather than on an IoT device. It is made of two parts:\n\n* The CounterFit app - this is a web app run locally where you can connect fake sensors and actuators to your virtual hardware\n* Shims - these are libraries that fake popular hardware APIs so you can take code that runs against well known hardware and run it against the CounterFit app.\n\n**This project is under construction**\n\nThis project is seriously under construction! Please let me know if you want to help.\n\n![Under development animated GIF](https://media.giphy.com/media/3o7qE1YN7aBOFPRw8E/giphy.gif)\n\n## Installing and running the app\n\n* Install the CounterFit app:\n\n    ```sh\n    pip install CounterFit\n    ```\n\n* Run the app:\n\n    ```sh\n    counterfit\n    ```\n\n* The app will launch, listening for web requests on port 5000, and open a web browser for you to start adding virtual sensors and actuators to your project\n\n### Running on a different port\n\nTo use a different port than the default 5000, set the `--port` option when you run the app:\n\n```sh\ncounterfit --port 5050\n```\n\n## Shims\n\nThe shims are designed to mimic the APIs for popular hardware components. The idea being you should be able to take code built against the shim and eventually run it on real hardware by changing the name of the package that is imported.\n\n### Available shims\n\n* ![Seeed Grove Py Shim](https://img.shields.io/badge/Platform-Python-green) [![Seeed Grove Py Shim](https://img.shields.io/badge/Shim-Grove.py-yellow)](./shims/SeeedStudios/grove/README.md) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-grove)](https://pypi.org/project/counterfit-shims-grove) [Grove.Py](https://github.com/Seeed-Studio/grove.py) shims that work with the [Seeed Grove ecosystem](https://www.seeedstudio.com/category/Grove-c-1003.html).\n\n* ![Seeed Grove DHT Shim](https://img.shields.io/badge/Platform-Python-green) [![Seeed DHT Shim](https://img.shields.io/badge/Shim-Seeed_DHT-yellow)](./shims/SeeedStudios/grove/README.md) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-seeed-python-dht)](https://pypi.org/project/counterfit-shims-seeed-python-dht) [Seeed DHT](https://github.com/Seeed-Studio/Seeed_Python_DHT) shims that work with the [Seeed DHT sensors](https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-DHT11.html).\n\n## Samples\n\nCheck out the [samples](./samples) directory for a range of samples.\n"
  },
  {
    "path": "counterfit-app/.pylintrc",
    "content": "[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=140\n\n[DESIGN]\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=13\n\n[MESSAGES CONTROL]\ndisable=trailing-whitespace,R0801,C0114,C0115,C0116"
  },
  {
    "path": "counterfit-app/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Jim Bennett\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "counterfit-app/MANIFEST.in",
    "content": "include src/CounterFit/static/*.*\ninclude src/CounterFit/static/images/*.*\ninclude src/CounterFit/templates/*.*"
  },
  {
    "path": "counterfit-app/README.md",
    "content": "# CounterFit\n\nIoT is great fun, but has a downside - hardware. You need access to a range of devices such as sensors and actuators to build your IoT projects. Sometimes you might have these devices, other times you may not - maybe you are waiting for a delivery, or parts are out of stock, or they are too expensive.\n\nThat's where this tool comes in.\n\n## What is CounterFit\n\nCounterFit is a tool that is designed to fake various IoT hardware components, such as LEDs, buttons, temperature sensors and the like, that you can then access from IoT device code running on your computer rather than on an IoT device. It is made of two parts:\n\n* The CounterFit app - this is a web app run locally where you can connect fake sensors and actuators to your virtual hardware\n* Shims - these are libraries that mimic popular hardware APIs so you can take code that runs against well known hardware and run it against the CounterFit app.\n\n## Getting started\n\n* Install the CounterFit app:\n\n    ```sh\n    pip install CounterFit\n    ```\n\n* Run the app:\n\n    ```sh\n    CounterFit\n    ```\n\n* The app will launch, listening for web requests on port 5000, and open a web browser for you to start adding virtual sensors and actuators to your project\n\n### Running on a different port\n\nTo use a different port than the default 5000, set the `--port` option when you run the app:\n\n```sh\nCounterFit --port 5050\n```\n\n## Connecting your code\n\nYou can connect your device code to CounterFit, using one of the available shims. See the [shim list for more details](https://github.com/CounterFit-IoT/CounterFit#shims).\n"
  },
  {
    "path": "counterfit-app/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "counterfit-app/requirements.txt",
    "content": "flask=2.1.2\nflask-socketio=5.2.0\neventlet=0.33.3\nsetuptools=65.5.0\ntwine=4.0.2\nwheel=0.38.4\npytest=7.2.2\npytest-runner=6.0.0"
  },
  {
    "path": "counterfit-app/setup.cfg",
    "content": "[metadata]\nname = CounterFit\nversion = attr: CounterFit.__version__\nurl = https://github.com/CounterFit-IoT/CounterFit\nproject_urls =\n    Documentation = https://github.com/CounterFit-IoT/CounterFit\n    Source Code = https://github.com/CounterFit-IoT/CounterFit\n    Issue Tracker = https://github.com/CounterFit-IoT/CounterFit/issues\n    Twitter = https://twitter.com/JimBobBennett\nlicense = MIT\nauthor = Jim Bennett\nmaintainer = Jim Bennett\ndescription = A virtual IoT hardware simulator.\nlong_description = file: README.md\nlong_description_content_type = text/markdown\nclassifiers =\n        Development Status :: 2 - Pre-Alpha\n        Intended Audience :: Developers\n        Topic :: System :: Hardware\n        License :: OSI Approved :: MIT License\n        Operating System :: OS Independent\n        Programming Language :: Python\n\n[options]\npackages = find:\npackage_dir = = src\ninclude_package_data = true\npython_requires = >= 3.8\n# Dependencies are in setup.py for GitHub's dependency graph.\n\n[options.packages.find]\nwhere = src\n\n[options.entry_points]\nconsole_scripts =\n    counterfit = CounterFit.counterfit:main\n\n[tool:pytest]\ntestpaths =\n    src/tests"
  },
  {
    "path": "counterfit-app/setup.py",
    "content": "from setuptools import setup\n\nsetup(\n    name='CounterFit',\n    install_requires=[\n        \"Flask==2.1.2\",\n        \"Flask-SocketIO==5.2.0\",\n        \"eventlet==0.33.3\"\n    ],\n    tests_require=['pytest==7.2.2'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/__init__.py",
    "content": "# pylint: disable=C0103\n\nfrom CounterFit.sensors import *\nfrom CounterFit.serial_sensors import *\nfrom CounterFit.binary_sensors import *\nfrom CounterFit.i2c_sensors import *\nfrom CounterFit.actuators import *\n\n__version__ = \"0.1.4.dev09\"\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/actuators.py",
    "content": "from abc import ABC, abstractmethod\nfrom enum import Enum\n\n\nclass ActuatorType(Enum):\n    FLOAT = 1\n    BOOLEAN = 2\n\n\nclass ActuatorBase(ABC):\n    def __init__(self, port: str):\n        self.__port = port\n\n    @staticmethod\n    @abstractmethod\n    def actuator_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def actuator_type() -> ActuatorType:\n        pass\n\n    @property\n    def port(self) -> str:\n        return self.__port\n\n    @property\n    # pylint: disable=invalid-name\n    def id(self) -> str:\n        return self.__port\n\n\nclass FloatActuatorBase(ActuatorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n        self.__value = 0\n\n    @staticmethod\n    @abstractmethod\n    def actuator_name() -> str:\n        pass\n\n    @staticmethod\n    def actuator_type() -> ActuatorType:\n        return ActuatorType.FLOAT\n\n    @property\n    def value(self) -> float:\n        return self.__value\n\n    @value.setter\n    def value(self, val: float):\n        self.__value = val\n\n\nclass BooleanActuatorBase(ActuatorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n\n        self.__value = False\n\n    @staticmethod\n    @abstractmethod\n    def actuator_name() -> str:\n        pass\n\n    @staticmethod\n    def actuator_type() -> ActuatorType:\n        return ActuatorType.BOOLEAN\n\n    @property\n    def value(self) -> bool:\n        return self.__value\n\n    @value.setter\n    def value(self, val: bool):\n        self.__value = val\n\n\nclass RelayActuator(BooleanActuatorBase):\n    @staticmethod\n    def actuator_name() -> str:\n        return \"Relay\"\n\n\nclass LedActuator(BooleanActuatorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n        self.__color = \"#FF0000\"\n\n    @staticmethod\n    def actuator_name() -> str:\n        return \"LED\"\n\n    @property\n    def color(self) -> str:\n        return self.__color\n\n    @color.setter\n    def color(self, val: str):\n        self.__color = val\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/binary_sensors.py",
    "content": "from abc import abstractmethod\nfrom enum import Enum\nimport io\n\nfrom CounterFit.sensors import SensorBase, SensorType\n\n\nclass BinarySensorBase(SensorBase):\n    def __init__(self, name: str):\n        super().__init__(name)\n        self.__value = io.BytesIO()\n        self._next_repeat_time = None\n        self._value_position = 0\n\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.BINARY\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @property\n    def id(self) -> str:\n        return self.port.replace(\"/\", \"\").replace(\" \", \"\")\n\n    @property\n    def value(self) -> io.BytesIO:\n        return self.__value\n\n    @value.setter\n    def value(self, val: io.BytesIO):\n        self.__value = val\n\n\nclass CameraImageSource(Enum):\n    FILE = 1\n    WEBCAM = 2\n\n\nclass CameraSensor(BinarySensorBase):\n    def __init__(self, name: str):\n        super().__init__(name)\n        self.__image_source = CameraImageSource.FILE\n        self.__image_file_name = \"\"\n        self.__web_cam_device_id = \"\"\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Camera\"\n\n    @property\n    def image_source(self) -> CameraImageSource:\n        return self.__image_source\n\n    @image_source.setter\n    def image_source(self, val: CameraImageSource):\n        self.__image_source = val\n\n    @property\n    def image_file_name(self) -> str:\n        return self.__image_file_name\n\n    @image_file_name.setter\n    def image_file_name(self, val: str):\n        self.__image_file_name = val\n\n    @property\n    def web_cam_device_id(self) -> str:\n        return self.__web_cam_device_id\n\n    @web_cam_device_id.setter\n    def web_cam_device_id(self, val: str):\n        self.__web_cam_device_id = val\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/counterfit.py",
    "content": "# pylint: disable=C0103,E0401,W0603\n\nimport argparse\nimport io\nimport json\nimport uuid\nimport webbrowser\nfrom base64 import b64decode, b64encode\nfrom threading import Timer\n\nfrom eventlet import event\nfrom eventlet.timeout import Timeout\n\nfrom flask import Flask, request, render_template\nfrom flask_socketio import SocketIO\n\nfrom CounterFit.sensors import SensorBase, SensorType\nfrom CounterFit.serial_sensors import GPSSensor, GPSValueType, SerialSensorBase\nfrom CounterFit.binary_sensors import BinarySensorBase, CameraImageSource, CameraSensor\nfrom CounterFit.actuators import ActuatorBase\n\napp = Flask(__name__)\napp.config[\"SECRET_KEY\"] = \"247783f3-bdda-4536-bffc-109e2464f10b\"\nsocketio = SocketIO(app)\n\nsensor_cache = {}\nactuator_cache = {}\n\nall_sensors = []\nall_actuators = []\n\nis_connected = False\n\n\ndef get_all_subclasses(cls, class_list):\n    for sub_class in cls.__subclasses__():\n        if len(sub_class.__abstractmethods__) == 0:\n            class_list.append(sub_class)\n\n        get_all_subclasses(sub_class, class_list)\n\n\nget_all_subclasses(SensorBase, all_sensors)\nget_all_subclasses(ActuatorBase, all_actuators)\n\nall_sensors = sorted(all_sensors, key=lambda s: s.sensor_name())\nall_actuators = sorted(all_actuators, key=lambda a: a.actuator_name())\n\n\n@app.route(\"/\", methods=[\"GET\"])\ndef home():\n    ports = []\n    ports_and__hex = []\n    for port in range(0, 127):\n        str_port = str(port)\n        if str_port not in sensor_cache and str_port not in actuator_cache:\n            ports.append(str_port)\n            ports_and__hex.append((str_port, f\"0x{port:02x}\"))\n\n    return render_template(\n        \"home.html\",\n        sensors=sensor_cache.values(),\n        actuators=actuator_cache.values(),\n        all_sensors=all_sensors,\n        all_actuators=all_actuators,\n        is_connected=is_connected,\n        ports=ports,\n        ports_and__hex=ports_and__hex,\n    )\n\n\ndef set_and_send_connected(connected: bool = True) -> None:\n    global is_connected\n    is_connected = connected\n    socketio.emit(\"device_connect\", {\"connected\": is_connected})\n\n\n@app.route(\"/connect\", methods=[\"POST\"])\ndef device_connect():\n    set_and_send_connected()\n\n    return \"OK\", 200\n\n\n@app.route(\"/disconnect\", methods=[\"POST\"])\ndef device_disconnect():\n    set_and_send_connected(False)\n\n    return \"OK\", 200\n\n\ndef create_pin_sensor(sensor, body):\n    port = str(body[\"pin\"])\n    unit = body[\"unit\"]\n\n    if sensor.sensor_type() == SensorType.FLOAT:\n        new_sensor = sensor(port, unit)\n    elif sensor.sensor_type() == SensorType.INTEGER:\n        new_sensor = sensor(port, unit)\n    else:\n        new_sensor = sensor(port)\n\n    sensor_cache[port.lower()] = new_sensor\n\n\ndef create_serial_sensor(sensor, body):\n    port = body[\"port\"]\n    new_sensor = sensor(port)\n    sensor_cache[port.lower()] = new_sensor\n\n\ndef create_binary_sensor(sensor, body):\n    name = body[\"name\"]\n    new_sensor = sensor(name)\n    sensor_cache[name.lower()] = new_sensor\n\n\ndef create_i2c_sensor(sensor, body):\n    port = str(body[\"i2c_pin\"])\n    unit = body[\"i2c_unit\"]\n    new_sensor = sensor(port, unit)\n    sensor_cache[port.lower()] = new_sensor\n\n\n@app.route(\"/create_sensor\", methods=[\"POST\"])\ndef create_sensor():\n    body = request.get_json()\n\n    print(\"Create sensor called:\", body)\n\n    sensor_type = body[\"type\"]\n\n    for sensor in all_sensors:\n        if sensor.sensor_name() == sensor_type:\n            if sensor.sensor_type() == SensorType.SERIAL:\n                create_serial_sensor(sensor, body)\n            elif sensor.sensor_type() == SensorType.BINARY:\n                create_binary_sensor(sensor, body)\n            elif sensor.sensor_type() == SensorType.I2C:\n                create_i2c_sensor(sensor, body)\n            else:\n                create_pin_sensor(sensor, body)\n\n    return \"OK\", 200\n\n\n@app.route(\"/create_actuator\", methods=[\"POST\"])\ndef create_actuator():\n    body = request.get_json()\n\n    print(\"Create actuator called:\", body)\n\n    actuator_type = body[\"type\"]\n    port = str(body[\"port\"])\n\n    for actuator in all_actuators:\n        if actuator.actuator_name() == actuator_type:\n            new_actuator = actuator(port)\n\n            actuator_cache[port.lower()] = new_actuator\n\n    return \"OK\", 200\n\n\n@app.route(\"/sensor_value\", methods=[\"GET\"])\ndef get_sensor_value():\n    set_and_send_connected()\n    port = str(request.args.get(\"port\", \"\"))\n    if port.lower() in sensor_cache:\n        sensor = sensor_cache[port.lower()]\n\n        response = {\"value\": sensor.value}\n        print(\"Returning sensor value\", response, \"for port\", port)\n\n        return json.dumps(response)\n\n    return \"Sensor with port \" + str(port) + \" not found\", 404\n\n\n@app.route(\"/serial_sensor_character\", methods=[\"GET\"])\ndef get_serial_sensor_character():\n    set_and_send_connected()\n    port = str(request.args.get(\"port\", \"\"))\n    if port.lower() in sensor_cache:\n        sensor: SerialSensorBase = sensor_cache[port.lower()]\n\n        response = {\"value\": sensor.read()}\n        print(\"Returning sensor value\", response, \"for port\", port)\n\n        return json.dumps(response)\n\n    return \"Sensor with port \" + str(port) + \" not found\", 404\n\n\n@app.route(\"/serial_sensor_line\", methods=[\"GET\"])\ndef get_serial_sensor_line():\n    set_and_send_connected()\n    port = str(request.args.get(\"port\", \"\"))\n    if port.lower() in sensor_cache:\n        sensor: SerialSensorBase = sensor_cache[port.lower()]\n\n        response = {\"value\": sensor.read_line()}\n        print(\"Returning sensor value\", response, \"for port\", port)\n\n        return json.dumps(response)\n\n    return \"Sensor with port \" + str(port) + \" not found\", 404\n\n\nevents = {}\n\n\ndef capture_camera_image_response(data):\n    try:\n        e = events[data[\"uuid\"]]\n        port = data[\"port\"]\n\n        image: str = data[\"image_base64\"]\n        image = image.replace(\"data:image/jpeg;base64,\", \"\").strip()\n        camera_sensor: CameraSensor = sensor_cache[port.lower()]\n        msg = b64decode(image)\n        camera_sensor.value = io.BytesIO(msg)\n\n        e.send(data)\n        del events[data[\"uuid\"]]\n    except KeyError:\n        pass\n\n\ndef capture_camera_image(sensor: CameraSensor, port) -> bool:\n    if sensor.image_source == CameraImageSource.WEBCAM:\n        u = str(uuid.uuid4())\n\n        req = {\"uuid\": u, \"port\": port}\n\n        socketio.emit(\n            \"capture_camera_from_webcam\" + str(port).replace(\"/\", \"\").replace(\" \", \"\"),\n            req,\n            callback=capture_camera_image_response,\n        )\n\n        timeout = Timeout(10)\n        try:\n            e = events[u] = event.Event()\n            e.wait(10)\n        except Timeout:\n            return False\n        finally:\n            events.pop(u, None)\n            timeout.cancel()\n\n    return True\n\n\n@app.route(\"/binary_sensor_data\", methods=[\"GET\"])\ndef get_binary_sensor_data():\n    set_and_send_connected()\n    port = str(request.args.get(\"port\", \"\"))\n    if port.lower() in sensor_cache:\n        if isinstance(sensor_cache[port.lower()], CameraSensor):\n            camera_sensor: CameraSensor = sensor_cache[port.lower()]\n            if not capture_camera_image(camera_sensor, port):\n                return (\n                    \"Timeout capturing camera image from webcam for camera \"\n                    + str(port),\n                    504,\n                )\n\n        sensor: BinarySensorBase = sensor_cache[port.lower()]\n\n        sensor.value.seek(0)\n        img_byte = sensor.value.getvalue()\n\n        response = {\"value\": b64encode(img_byte).decode()}\n        print(\"Returning sensor value\", str(response)[0:500], \"for port\", port)\n\n        return json.dumps(response)\n\n    return \"Sensor with port \" + str(port) + \" not found\", 404\n\n\n@app.route(\"/delete_sensor\", methods=[\"POST\"])\ndef delete_sensor():\n    body = request.get_json()\n\n    print(\"Delete sensor called:\", body)\n\n    port = body[\"port\"]\n\n    if port.lower() in sensor_cache:\n        del sensor_cache[port.lower()]\n\n    return \"OK\", 200\n\n\n@app.route(\"/delete_actuator\", methods=[\"POST\"])\ndef delete_actuator():\n    body = request.get_json()\n\n    print(\"Delete actuator called:\", body)\n\n    port = body[\"port\"]\n\n    if port.lower() in actuator_cache:\n        del actuator_cache[port.lower()]\n\n    return \"OK\", 200\n\n\n@app.route(\"/float_sensor_settings\", methods=[\"POST\"])\ndef set_float_sensor_settings():\n    body = request.get_json()\n\n    print(\"Float sensor settings called:\", body)\n\n    port = body[\"port\"]\n    value = body[\"value\"]\n    is_random = body[\"is_random\"]\n    random_min = body[\"random_min\"]\n    random_max = body[\"random_max\"]\n\n    if port.lower() in sensor_cache:\n        sensor = sensor_cache[port.lower()]\n        sensor.value = value\n        sensor.random = is_random\n        sensor.random_min = random_min\n        sensor.random_max = random_max\n\n    return \"OK\", 200\n\n\n@app.route(\"/integer_sensor_settings\", methods=[\"POST\"])\ndef set_integer_sensor_settings():\n    body = request.get_json()\n\n    print(\"Integer sensor settings called:\", body)\n\n    port = body[\"port\"]\n    value = body[\"value\"]\n    is_random = body[\"is_random\"]\n    random_min = body[\"random_min\"]\n    random_max = body[\"random_max\"]\n\n    if port.lower() in sensor_cache:\n        sensor = sensor_cache[port.lower()]\n        sensor.value = value\n        sensor.random = is_random\n        sensor.random_min = random_min\n        sensor.random_max = random_max\n\n    return \"OK\", 200\n\n\n@app.route(\"/led_actuator_settings\", methods=[\"POST\"])\ndef set_led_actuator_settings():\n    body = request.get_json()\n\n    print(\"LED actuator settings called:\", body)\n\n    port = body[\"port\"]\n    color = body[\"color\"]\n\n    if port.lower() in actuator_cache:\n        actuator = actuator_cache[port.lower()]\n        actuator.color = color\n\n    return \"OK\", 200\n\n\n@app.route(\"/boolean_sensor_settings\", methods=[\"POST\"])\ndef set_boolean_sensor_settings():\n    body = request.get_json()\n\n    print(\"Boolean sensor settings called:\", body)\n\n    port = body[\"port\"]\n    value = body[\"value\"]\n    is_random = body[\"is_random\"]\n\n    if port.lower() in sensor_cache:\n        sensor = sensor_cache[port.lower()]\n        sensor.value = value\n        sensor.random = is_random\n\n    return \"OK\", 200\n\n\n@app.route(\"/gps_sensor_settings\", methods=[\"POST\"])\ndef set_gps_sensor_settings():\n    body = request.get_json()\n\n    print(\"GPS sensor settings called:\", body)\n\n    port = body[\"port\"]\n\n    if port.lower() in sensor_cache:\n        sensor: GPSSensor = sensor_cache[port.lower()]\n        sensor.repeat = body[\"repeat\"]\n        source = body[\"source\"]\n\n        if source == \"latlon\":\n            sensor.value_type = GPSValueType.LATLON\n            sensor.lat = body[\"lat\"]\n            sensor.lon = body[\"lon\"]\n            sensor.number_of_satellites = body[\"number_of_satellites\"]\n        elif source == \"nmeasentences\":\n            sensor.value_type = GPSValueType.NMEA\n            sensor.raw_nmea = body[\"nmea\"]\n        else:\n            sensor.value_type = GPSValueType.GPX\n            sensor.gpx_file_contents = body[\"gpx\"]\n            sensor.gpx_file_name = body[\"gpx_file_name\"]\n\n    return \"OK\", 200\n\n\n@app.route(\"/camera_sensor_settings\", methods=[\"POST\"])\ndef set_camera_sensor_settings():\n    body = request.get_json()\n\n    print(\"Camera sensor settings called:\", str(body)[0:500])\n\n    port = body[\"port\"]\n\n    if port.lower() in sensor_cache:\n        sensor: CameraSensor = sensor_cache[port.lower()]\n        sensor.image_source = (\n            CameraImageSource.FILE\n            if body[\"source\"] == \"File\"\n            else CameraImageSource.WEBCAM\n        )\n\n        if sensor.image_source == CameraImageSource.FILE:\n            sensor.image_file_name = body[\"image_file_name\"]\n            msg = b64decode(body[\"file_contents\"])\n            sensor.value = io.BytesIO(msg)\n        else:\n            sensor.web_cam_device_id = body[\"web_cam_device_id\"]\n\n    return \"OK\", 200\n\n\n@app.route(\"/sensor_units\", methods=[\"POST\"])\ndef get_sensor_units():\n    body = request.get_json()\n\n    print(\"Sensor units called:\", body)\n\n    sensor_type = body[\"type\"]\n\n    # pylint: disable=R1705\n    for sensor in all_sensors:\n        if sensor.sensor_name() == sensor_type:\n            if (\n                sensor.sensor_type() == SensorType.FLOAT\n                or sensor.sensor_type() == SensorType.INTEGER\n                or sensor.sensor_type() == SensorType.I2C\n            ):\n                return {\"units\": sensor.sensor_units()}\n            else:\n                return {\"units\": []}\n\n    return \"Not found\", 404\n\n\n@app.route(\"/actuator_value\", methods=[\"POST\"])\ndef set_actuator_value():\n    set_and_send_connected()\n    port = str(request.args.get(\"port\", \"\"))\n    body = request.get_json()\n\n    print(\"Actuator value called:\", body, \"for port\", port)\n\n    value = body[\"value\"]\n\n    if port.lower() in actuator_cache:\n        actuator = actuator_cache[port.lower()]\n        actuator.value = value\n\n        socketio.emit(\"actuator_change\" + str(port), {\"port\": port, \"value\": value})\n\n    return \"OK\", 200\n\n\ndef open_browser(port):\n    webbrowser.open_new(f\"http://127.0.0.1:{port}/\")\n\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--port\", metavar=\"port\", type=int, default=5000, help=\"the port to run on\"\n    )\n    parser.add_argument(\n        \"--dontopen\",\n        action=\"store_true\",\n        help=\"If this is present, CounterFit is not automatically opened in a browser\",\n    )\n\n    args = parser.parse_args()\n\n    print(f\"CounterFit - virtual IoT hardware running on port {args.port}\")\n\n    if args.dontopen is None:\n        print(\"Loading browser...\")\n        Timer(3, open_browser, [args.port]).start()\n\n    socketio.run(app, host=\"0.0.0.0\", port=args.port)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/i2c_sensors.py",
    "content": "from abc import abstractmethod\nfrom enum import Enum\nfrom typing import List\nimport random\n\nfrom CounterFit.sensors import SensorBase, SensorType\n\n\nclass I2CSensorBase(SensorBase):\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.I2C\n\n    @staticmethod\n    @abstractmethod\n    def sensor_unit_type() -> SensorType:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @property\n    def id(self) -> str:\n        return self.port\n\n    @property\n    def address(self) -> str:\n        return f\"0x{int(self.port):02x}\"\n\n\nclass FloatI2CSensorBase(I2CSensorBase):\n    def __init__(self, port: str, valid_min: float, valid_max: float):\n        super().__init__(port)\n\n        self.__valid_min = valid_min\n        self.__valid_max = valid_max\n        self.value = valid_min\n        self.random_min = float(valid_min)\n        self.random_max = float(valid_max)\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_units() -> List[str]:\n        pass\n\n    @staticmethod\n    def sensor_unit_type() -> SensorType:\n        return SensorType.FLOAT\n\n    @property\n    @abstractmethod\n    def unit(self) -> str:\n        pass\n\n    @property\n    def value(self) -> float:\n        if self._random:\n            return round(random.uniform(self.__random_min, self.__random_max), 2)\n\n        return self.__value\n\n    @value.setter\n    def value(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__value = val\n\n    @property\n    def random_min(self) -> float:\n        return self.__random_min\n\n    @random_min.setter\n    def random_min(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_min = val\n\n    @property\n    def random_max(self) -> float:\n        return self.__random_max\n\n    @random_max.setter\n    def random_max(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_max = val\n\n    @property\n    def valid_min(self) -> float:\n        return self.__valid_min\n\n    @property\n    def valid_max(self) -> float:\n        return self.__valid_max\n\n\nclass IntegerI2CSensorBase(I2CSensorBase):\n    def __init__(self, port: str, valid_min: int, valid_max: int):\n        super().__init__(port)\n\n        self.__valid_min = valid_min\n        self.__valid_max = valid_max\n        self.value = valid_min\n        self.random_min = int(valid_min)\n        self.random_max = int(valid_max)\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_units() -> List[str]:\n        pass\n\n    @staticmethod\n    def sensor_unit_type() -> SensorType:\n        return SensorType.INTEGER\n\n    @property\n    @abstractmethod\n    def unit(self) -> str:\n        pass\n\n    @property\n    def value(self) -> int:\n        if self._random:\n            return random.randint(self.__random_min, self.__random_max)\n\n        return self.__value\n\n    @value.setter\n    def value(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__value = val\n\n    @property\n    def random_min(self) -> int:\n        return self.__random_min\n\n    @random_min.setter\n    def random_min(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_min = val\n\n    @property\n    def random_max(self) -> int:\n        return self.__random_max\n\n    @random_max.setter\n    def random_max(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_max = val\n\n    @property\n    def valid_min(self) -> int:\n        return self.__valid_min\n\n    @property\n    def valid_max(self) -> int:\n        return self.__valid_max\n\n\n# pylint: disable=C0103\nclass DistanceUnit(Enum):\n    Millimeter = 1\n\n\nclass DistanceSensor(IntegerI2CSensorBase):\n    def __init__(self, port: str, unit):\n        if isinstance(unit, str):\n            unit = DistanceUnit[unit]\n\n        self.__unit = unit\n\n        super().__init__(port, 0, 999999)\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Distance\"\n\n    @property\n    def unit(self) -> str:\n        return self.__unit.name\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [DistanceUnit.Millimeter.name]\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/sensors.py",
    "content": "from abc import ABC, abstractmethod\nfrom enum import Enum\nfrom typing import List\nimport random\n\n\nclass SensorType(Enum):\n    FLOAT = 1\n    INTEGER = 2\n    BOOLEAN = 3\n    SERIAL = 4\n    BINARY = 5\n    I2C = 6\n\n\nclass SensorBase(ABC):\n    def __init__(self, port: str):\n        self.__port = port\n        self._random = False\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_type() -> SensorType:\n        pass\n\n    @property\n    # pylint: disable=invalid-name\n    def id(self) -> str:\n        return self.__port\n\n    @property\n    def port(self) -> str:\n        return self.__port\n\n    @property\n    def random(self) -> bool:\n        return self._random\n\n    @random.setter\n    def random(self, val: bool):\n        self._random = val\n\n\n# pylint: disable=C0103\nclass DefaultUnit(Enum):\n    NoUnits = 1\n\n\n# pylint: disable=C0103\nclass PercentUnit(Enum):\n    Percent = 1\n\n\nclass FloatSensorBase(SensorBase):\n    def __init__(self, port: str, valid_min: float, valid_max: float):\n        super().__init__(port)\n\n        self.__valid_min = valid_min\n        self.__valid_max = valid_max\n        self.value = valid_min\n        self.random_min = float(valid_min)\n        self.random_max = float(valid_max)\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_units() -> List[str]:\n        pass\n\n    @property\n    @abstractmethod\n    def unit(self) -> str:\n        pass\n\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.FLOAT\n\n    @property\n    def value(self) -> float:\n        if self._random:\n            return round(random.uniform(self.__random_min, self.__random_max), 2)\n\n        return self.__value\n\n    @value.setter\n    def value(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__value = val\n\n    @property\n    def random_min(self) -> float:\n        return self.__random_min\n\n    @random_min.setter\n    def random_min(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_min = val\n\n    @property\n    def random_max(self) -> float:\n        return self.__random_max\n\n    @random_max.setter\n    def random_max(self, val: float):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_max = val\n\n    @property\n    def valid_min(self) -> float:\n        return self.__valid_min\n\n    @property\n    def valid_max(self) -> float:\n        return self.__valid_max\n\n\nclass IntegerSensorBase(SensorBase):\n    def __init__(self, port: str, valid_min: int, valid_max: int):\n        super().__init__(port)\n\n        self.__valid_min = valid_min\n        self.__valid_max = valid_max\n        self.value = valid_min\n        self.random_min = int(valid_min)\n        self.random_max = int(valid_max)\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    @abstractmethod\n    def sensor_units() -> List[str]:\n        pass\n\n    @property\n    @abstractmethod\n    def unit(self) -> str:\n        pass\n\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.INTEGER\n\n    @property\n    def value(self) -> int:\n        if self._random:\n            return random.randint(self.__random_min, self.__random_max)\n\n        return self.__value\n\n    @value.setter\n    def value(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__value = val\n\n    @property\n    def random_min(self) -> int:\n        return self.__random_min\n\n    @random_min.setter\n    def random_min(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_min = val\n\n    @property\n    def random_max(self) -> int:\n        return self.__random_max\n\n    @random_max.setter\n    def random_max(self, val: int):\n        if val < self.__valid_min or val > self.__valid_max:\n            raise ValueError()\n        self.__random_max = val\n\n    @property\n    def valid_min(self) -> int:\n        return self.__valid_min\n\n    @property\n    def valid_max(self) -> int:\n        return self.__valid_max\n\n\nclass BooleanSensorBase(SensorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n\n        self.value = False\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.BOOLEAN\n\n    @property\n    def value(self) -> bool:\n        if self._random:\n            return random.choice([True, False])\n\n        return self.__value\n\n    @value.setter\n    def value(self, val: bool):\n        self.__value = val\n\n\n# pylint: disable=C0103\nclass TemperatureUnit(Enum):\n    Celsius = 1\n    Fahrenheit = 2\n    Kelvin = 3\n\n\nclass TemperatureSensor(FloatSensorBase):\n    def __init__(self, port: str, unit):\n        if isinstance(unit, str):\n            unit = TemperatureUnit[unit]\n\n        self.__unit = unit\n\n        if self.__unit == TemperatureUnit.Celsius:\n            valid_min = -273.15\n        elif self.__unit == TemperatureUnit.Fahrenheit:\n            valid_min = -459.67\n        else:\n            valid_min = 0\n\n        super().__init__(port, valid_min, 999999999.0)\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Temperature\"\n\n    @property\n    def unit(self) -> str:\n        return self.__unit.name\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [\n            TemperatureUnit.Celsius.name,\n            TemperatureUnit.Fahrenheit.name,\n            TemperatureUnit.Kelvin.name,\n        ]\n\n\nclass HumiditySensor(FloatSensorBase):\n    def __init__(self, port: str, unit):\n        if isinstance(unit, str):\n            unit = PercentUnit[unit]\n\n        self.__unit = unit\n\n        super().__init__(port, 0.0, 100.0)\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Humidity\"\n\n    @property\n    def unit(self) -> str:\n        return self.__unit.name\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [PercentUnit.Percent.name]\n\n\n# pylint: disable=C0103,C0102\nclass PressureUnit(Enum):\n    kPa = 1\n    torr = 2\n    atm = 3\n    bar = 4\n\n\nclass PressureSensor(FloatSensorBase):\n    def __init__(self, port: str, unit):\n        if isinstance(unit, str):\n            unit = PressureUnit[unit]\n\n        self.__unit = unit\n\n        super().__init__(port, 0, 999999999.0)\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Pressure\"\n\n    @property\n    def unit(self) -> str:\n        return self.__unit.name\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [\n            PressureUnit.kPa.name,\n            PressureUnit.torr.name,\n            PressureUnit.atm.name,\n            PressureUnit.bar.name,\n        ]\n\n\nclass AnalogSensor(IntegerSensorBase):\n    # pylint: disable=W0613\n    def __init__(self, port: str, unit):\n        super().__init__(port, 0, 1023)\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @property\n    def unit(self) -> str:\n        return DefaultUnit.NoUnits.name\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [DefaultUnit.NoUnits.name]\n\n\nclass LightSensor(AnalogSensor):\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Light\"\n\n\nclass UVSensor(AnalogSensor):\n    @staticmethod\n    def sensor_name() -> str:\n        return \"UV\"\n\n\nclass IRSensor(AnalogSensor):\n    @staticmethod\n    def sensor_name() -> str:\n        return \"IR\"\n\n\nclass SoilMoistureSensor(AnalogSensor):\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Soil Moisture\"\n\n\nclass ButtonSensor(BooleanSensorBase):\n    @staticmethod\n    def sensor_name() -> str:\n        return \"Button\"\n\n    @staticmethod\n    def sensor_units() -> List[str]:\n        return [DefaultUnit.NoUnits.name]\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/serial_sensors.py",
    "content": "from abc import abstractmethod\nfrom enum import Enum\nimport datetime\nimport re\nfrom xml.dom.minidom import Element, parseString\n\nfrom CounterFit.sensors import SensorBase, SensorType\n\n\nclass SerialSensorBase(SensorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n\n        self.__value = \"\"\n        self.__repeat = False\n        self._next_repeat_time = None\n        self._value_position = 0\n\n    @staticmethod\n    def sensor_type() -> SensorType:\n        return SensorType.SERIAL\n\n    @staticmethod\n    @abstractmethod\n    def sensor_name() -> str:\n        pass\n\n    @property\n    def id(self) -> str:\n        return self.port.replace(\"/\", \"\").replace(\" \", \"\")\n\n    @property\n    def value(self) -> str:\n        return self.__value\n\n    @value.setter\n    def value(self, val: str):\n        self.__value = val\n        self._value_position = 0\n\n    @property\n    def repeat(self) -> bool:\n        return self.__repeat\n\n    @repeat.setter\n    def repeat(self, val: bool):\n        self.__repeat = val\n\n    def read(self):\n        current_utc = datetime.datetime.utcnow()\n        if self._next_repeat_time is None:\n            self._next_repeat_time = current_utc + datetime.timedelta(0, 1)\n\n        if self._value_position >= len(self.value):\n            if self.repeat and current_utc > self._next_repeat_time:\n                self._next_repeat_time = current_utc\n                self._value_position = 0\n\n        if self._value_position >= len(self.value):\n            return \"\"\n\n        char = self.value[self._value_position]\n        self._value_position += 1\n        return char\n\n    def read_line(self):\n        line = \"\"\n        char = self.read()\n\n        while char not in (\"\\n\", \"\"):\n            line = line + char\n            char = self.read()\n\n        return line\n\n\nclass GPSValueType(Enum):\n    LATLON = 1\n    NMEA = 2\n    GPX = 3\n\n\n# pylint: disable=too-many-instance-attributes\nclass GPSSensor(SerialSensorBase):\n    def __init__(self, port: str):\n        super().__init__(port)\n        self.__value_type = GPSValueType.LATLON\n        self.__lat = 0.0\n        self.__lon = 0.0\n        self.__number_of_satellites = 0\n        self.__raw_nmea = \"\"\n        self.__gpx_file_name = \"\"\n        self.__gpx_file_contents = \"\"\n        self.__substitute_value = \"\"\n        self.__substitute_start_position = 0\n        self.__substitute_end_position = 0\n        self.__next_gpgga_line_time = None\n\n    @staticmethod\n    def sensor_name() -> str:\n        return \"UART GPS\"\n\n    @staticmethod\n    def _decimal_decrees_to_ddmmm(decimal_degrees: float) -> str:\n        decimal_degrees = abs(decimal_degrees)\n        degrees = int(decimal_degrees)\n        minutes = (float(decimal_degrees) - float(degrees)) * 60\n\n        minutes_string = f\"{minutes:.8f}\".zfill(11).rstrip(\"0\")\n        if minutes_string.endswith(\".\"):\n            minutes_string += \"0\"\n\n        return f\"{degrees}{minutes_string}\"\n\n    @staticmethod\n    def _build_checksum(sentence: str) -> str:\n        checksum_data = re.sub(\n            \"(\\n|\\r\\n)\", \"\", sentence[sentence.find(\"$\") + 1 : sentence.find(\"*\")]\n        )\n\n        checksum = 0\n\n        for character in checksum_data:\n            checksum ^= ord(character)\n\n        return hex(checksum).replace(\"0x\", \"\").upper().zfill(2)\n\n    @staticmethod\n    def _build_sentence_from_lat_lon_num_satellites(\n        lat: float, lon: float, num_satellites: int\n    ) -> str:\n        converted_lat = GPSSensor._decimal_decrees_to_ddmmm(lat)\n        converted_lon = GPSSensor._decimal_decrees_to_ddmmm(lon)\n        lat_dir = \"N\" if lat > 0 else \"S\"\n        lon_dir = \"E\" if lon > 0 else \"W\"\n\n        # use a timestamp of xxxxxx.xx, and this will be replaced with the current time when the value is requested\n        # Use a checksum of zz, and again it will be replaces\n        return f\"$GPGGA,xxxxxx.xx,{converted_lat},{lat_dir},{converted_lon},{lon_dir},1,{num_satellites},,0,M,0,M,,*zz\\n\"\n\n    def _build_value(self) -> None:\n        if self.value_type == GPSValueType.LATLON:\n            self.value = GPSSensor._build_sentence_from_lat_lon_num_satellites(\n                self.lat, self.lon, self.number_of_satellites\n            )\n        if self.value_type == GPSValueType.NMEA:\n            self.value = self.raw_nmea\n            if not self.value.endswith(\"\\n\"):\n                self.value += \"\\n\"\n        if self.value_type == GPSValueType.GPX:\n            self.value = \"\"\n\n            if self.gpx_file_contents:\n                document = parseString(self.gpx_file_contents)\n                track_parts = document.getElementsByTagName(\"trkpt\")\n                track_part: Element\n                for track_part in track_parts:\n                    self.value += GPSSensor._build_sentence_from_lat_lon_num_satellites(\n                        float(track_part.getAttribute(\"lat\")),\n                        float(track_part.getAttribute(\"lon\")),\n                        3,\n                    )\n\n    def read(self):\n        current_utc = datetime.datetime.utcnow()\n\n        if self._next_repeat_time is None:\n            self._next_repeat_time = current_utc + datetime.timedelta(0, 1)\n\n        if self.__next_gpgga_line_time is None:\n            self.__next_gpgga_line_time = current_utc + datetime.timedelta(0, 1)\n\n        # only repeat after a delay\n        if self._value_position >= len(self.value):\n            if self.repeat and current_utc > self._next_repeat_time:\n                self._next_repeat_time = current_utc + datetime.timedelta(0, 1)\n                self._value_position = 0\n\n        if self._value_position >= len(self.value):\n            return \"\"\n\n        chars_from_position = self.value[self._value_position :]\n\n        # if we are a newline right before a $GPGGA then wait one second before sending it to space out GPX files or long NMEA data files\n        if (\n            self._value_position > 0\n            and chars_from_position.startswith(\"$\")\n            and chars_from_position[3:].startswith(\"GGA\")\n        ):\n            if current_utc > self.__next_gpgga_line_time:\n                self.__next_gpgga_line_time = current_utc + datetime.timedelta(0, 1)\n            else:\n                return \"\"\n\n        if chars_from_position.startswith(\"$GPGGA,xxxxxx.xx\"):\n            self.__substitute_start_position = self._value_position\n\n            # Find the end checksum\n            self.__substitute_end_position = self.value.find(\n                \"*zz\", self._value_position\n            ) + len(\"*zz\")\n\n            # find the block the right length\n            self.__substitute_value = self.value[\n                self.__substitute_start_position : self.__substitute_end_position\n            ]\n\n            current_utc = datetime.datetime.utcnow()\n            time_stamp = f\"{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00\"\n            self.__substitute_value = self.__substitute_value.replace(\n                \"xxxxxx.xx\", time_stamp\n            )\n\n            checksum = GPSSensor._build_checksum(self.__substitute_value)\n            self.__substitute_value = self.__substitute_value.replace(\n                \"*zz\", \"*\" + checksum\n            )\n\n        if (\n            self.__substitute_start_position <= self._value_position\n            and self.__substitute_end_position > self._value_position\n        ):\n            next_char = self.__substitute_value[\n                self._value_position - self.__substitute_start_position\n            ]\n            self._value_position += 1\n            return next_char\n\n        self.__substitute_start_position = 0\n        self.__substitute_end_position = 0\n        self.__substitute_value = \"\"\n\n        return super().read()\n\n    @property\n    def value_type(self) -> GPSValueType:\n        return self.__value_type\n\n    @value_type.setter\n    def value_type(self, val: GPSValueType):\n        self.__value_type = val\n        self._build_value()\n\n    @property\n    def lat(self) -> float:\n        return self.__lat\n\n    @lat.setter\n    def lat(self, val: float):\n        self.__lat = val\n        self._build_value()\n\n    @property\n    def lon(self) -> float:\n        return self.__lon\n\n    @lon.setter\n    def lon(self, val: float):\n        self.__lon = val\n        self._build_value()\n\n    @property\n    def number_of_satellites(self) -> int:\n        return self.__number_of_satellites\n\n    @number_of_satellites.setter\n    def number_of_satellites(self, val: int):\n        self.__number_of_satellites = val\n        self._build_value()\n\n    @property\n    def raw_nmea(self) -> str:\n        return self.__raw_nmea\n\n    @raw_nmea.setter\n    def raw_nmea(self, val: str):\n        self.__raw_nmea = val\n        self._build_value()\n\n    @property\n    def gpx_file_name(self) -> str:\n        return self.__gpx_file_name\n\n    @gpx_file_name.setter\n    def gpx_file_name(self, val: str):\n        self.__gpx_file_name = val\n        self._build_value()\n\n    @property\n    def gpx_file_contents(self) -> str:\n        return self.__gpx_file_contents\n\n    @gpx_file_contents.setter\n    def gpx_file_contents(self, val: str):\n        self.__gpx_file_contents = val\n        self._build_value()\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/static/style.css",
    "content": ".sensor_container {\n}\n\n.actuator_container {\n}\n\n#circle {\n    width: 50px;\n    height: 50px;\n    -webkit-border-radius: 25px;\n    -moz-border-radius: 25px;\n    border-radius: 25px;\n    background: red;\n  }"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/binary_sensor_create_settings.html",
    "content": "<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_sensor_port\">Name:</label>\n    </div>\n    <div class=\"col\">\n        <input class=\"form-control\" type=\"text\" id=\"new_sensor_name\" value=\"sensor_1\" style=\"width: 100%;\"/>\n    </div>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/boolean_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Pin {{sensor.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <label for=\"value{{sensor.id}}\">Value:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"value{{sensor.id}}\" value=\"{{sensor.value}}\"\n                        {% if sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"random{{sensor.id}}\">Random:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"random{{sensor.id}}\" \n                        {% if sensor.random: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n            var is_random_control = document.getElementById(\"random{{sensor.id}}\")\n            var value_control = document.getElementById(\"value{{sensor.id}}\")\n\n            is_random_control.addEventListener(\"change\", function() {\n                value_control.disabled = is_random_control.checked\n            })\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var is_random = is_random_control.checked\n                var value = value_control.checked\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    'value': value,\n                    'is_random': is_random\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./boolean_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/camera_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">{{sensor.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"source{{sensor.id}}\">Source:</label>\n                </div>\n                <div class=\"col\">\n                    <select name=\"source{{sensor.id}}\" id=\"source{{sensor.id}}\" class=\"form-control\">\n                        <option value=\"File\"\n                            {% if sensor.image_source.name == \"FILE\": %}\n                                selected\n                            {% endif %}\n                        >File</option>\n                        <option value=\"WebCam\"\n                            {% if sensor.image_source.name == \"WEBCAM\": %}\n                                selected\n                            {% endif %}\n                        >WebCam</option>\n                    </select>\n                </div>\n            </div>\n\n            <div id=\"camera_file_settings{{sensor.id}}\"\n                {% if sensor.image_source.name == \"FILE\": %}\n                    style=\"display:block\"\n                {% else %}\n                    style=\"display:none\"\n                {% endif %}\n            >\n                <div class=\"row\" style=\"margin-top: 20px;\">\n\n                    <div class=\"col\">\n                        <label for=\"image_file_name{{sensor.id}}\">Image File:</label>\n                    </div>\n                    <div class=\"col\">\n                        {{sensor.image_file_name}}\n                    </div>\n                </div>\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <input class=\"form-control\" type=\"file\" id=\"image_file_name{{sensor.id}}\" value=\"\" style=\"width: 100%;\"/>\n                </div>\n            </div>\n\n            <div id=\"web_cam_settings{{sensor.id}}\"\n                {% if sensor.image_source.name == \"FILE\": %}\n                    style=\"display:none\"\n                {% else %}\n                    style=\"display:block\"\n                {% endif %}\n            >\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <div class=\"col\">\n                        <label for=\"web_cam_source{{sensor.id}}\">Camera:</label>\n                    </div>\n                    <div class=\"col\">\n                        <select name=\"web_cam_source{{sensor.id}}\" id=\"web_cam_source{{sensor.id}}\" class=\"form-control\">\n                           \n                        </select>\n                    </div>\n                </div>\n\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <video id=\"web_cam{{sensor.id}}\" autoplay style=\"width: 100%;\"/></video>\n                </div>\n            </div>\n\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var web_cam = document.getElementById(\"web_cam{{sensor.id}}\");\n            var web_cam_source = document.getElementById(\"web_cam_source{{sensor.id}}\");\n            var running_video = false;\n\n            var source_control = document.getElementById(\"source{{sensor.id}}\")\n\n            var camera_file_settings_control = document.getElementById(\"camera_file_settings{{sensor.id}}\")\n            var web_cam_settings_control = document.getElementById(\"web_cam_settings{{sensor.id}}\")\n\n            var image_file_name_control = document.getElementById(\"image_file_name{{sensor.id}}\")\n\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n\n            var get_devices = function (mediaDevices) {\n                web_cam_source.innerHTML = '';\n                let count = 1;\n                mediaDevices.forEach(mediaDevice => {\n                    if (mediaDevice.kind === 'videoinput') {\n                        const option = document.createElement('option');\n                        option.value = mediaDevice.deviceId;\n                        const label = mediaDevice.label || `Camera ${count++}`;\n                        const textNode = document.createTextNode(label);\n                        option.appendChild(textNode);\n                        web_cam_source.appendChild(option);\n                    }\n                });\n            }\n\n            var stop_media_tracks = function (stream) {\n                stream.getTracks().forEach(track => {\n                    track.stop();\n                });\n            }\n\n            var select_web_cam = async function (device_id) {\n                if (web_cam.srcObject) {\n                    stop_media_tracks(web_cam.srcObject);\n                }\n\n                var selected_device_Id = {\n                    deviceId : { exact: device_id },\n                    width: { ideal: 4096 },\n                    height: { ideal: 2160 } \n                }\n\n                const constraints = {\n                    video: selected_device_Id,\n                    audio: false\n                };\n\n                web_cam.srcObject = await navigator.mediaDevices.getUserMedia(constraints)\n                web_cam.play();\n            }\n\n            var set_web_cam_settings = async function () {\n                var selected_source = source_control.value\n\n                if (selected_source == \"File\") {\n                    camera_file_settings_control.style.display = \"block\"\n                    web_cam_settings_control.style.display = \"none\"\n                }\n                else {\n                    camera_file_settings_control.style.display = \"none\"\n                    web_cam_settings_control.style.display = \"block\"\n\n                    if (!running_video) {\n                        running_video = true;\n                        if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n                            web_cam.srcObject = await navigator.mediaDevices.getUserMedia({ video: true })\n                            navigator.mediaDevices.enumerateDevices().then(get_devices);\n\n                            web_cam_source.addEventListener(\"change\", async function() {\n                                var selected_web_cam_source = web_cam_source.value\n                                await select_web_cam(selected_web_cam_source)\n                            })\n\n                            web_cam.play();\n                        }\n                    }\n                }\n            }\n\n            if (source_control.value == 'WebCam') {\n                const select_web_cam_for_device = async () => {\n                    await set_web_cam_settings()\n                    device_id = '{{sensor.web_cam_device_id}}'\n                    if (device_id) {                        \n                        await select_web_cam(device_id)\n                        web_cam_source.value = device_id\n                    }\n                }\n                select_web_cam_for_device()\n            }\n\n            source_control.addEventListener(\"change\", set_web_cam_settings)\n\n            var array_buffer_to_base64 = function(buffer) {\n                var binary = '';\n                var bytes = new Uint8Array( buffer );\n                var len = bytes.byteLength;\n                for (var i = 0; i < len; i++) {\n                    binary += String.fromCharCode( bytes[ i ] );\n                }\n                return window.btoa( binary );\n            }\n\n            var post_camera_sensor_settings = function (payload) {\n                var xhr = new XMLHttpRequest();\n                var url = \"./camera_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            }\n\n            set_button.addEventListener(\"click\", function() {\n                var selected_source = source_control.value\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    \"source\": selected_source\n                }\n\n                if (selected_source == \"File\") {\n                    const file = image_file_name_control.files[0];\n                    payload[\"image_file_name\"] = file.name\n\n                    const reader = new FileReader();\n                    reader.onload = function(event)\n                    {\n                        var contents = event.target.result;\n                        payload[\"file_contents\"] = array_buffer_to_base64(contents);\n                        post_camera_sensor_settings(payload)\n                    };\n                    \n                    reader.readAsArrayBuffer(file);\n                }\n                else {\n                    var selected_web_cam_source = web_cam_source.value\n                    payload[\"web_cam_device_id\"] = selected_web_cam_source\n                    post_camera_sensor_settings(payload)\n                }\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n\n            var socket = io();\n            socket.on('capture_camera_from_webcam{{sensor.id}}', function(data, ack) {\n                console.log('Camera capture received');\n\n                var canvas = document.createElement('canvas');\n                canvas.width = web_cam.videoWidth;\n                canvas.height = web_cam.videoHeight;\n\n                var context = canvas.getContext('2d');\n                context.drawImage(web_cam, 0, 0, canvas.width, canvas.height);\n\n                canvas.toBlob\n\n                data['image_base64'] = canvas.toDataURL(\"image/jpeg\")\n                ack(data);\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/float_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Pin {{sensor.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    Units:\n                </div>\n                <div class=\"col\">\n                    {{ sensor.unit }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    Value range:\n                </div>\n                <div class=\"col\">\n                    {{ \"{:,.2f}\".format(sensor.valid_min) }} to {{ \"{:,.2f}\".format(sensor.valid_max) }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"value{{sensor.id}}\">Value:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"value{{sensor.id}}\" value=\"{{sensor.value}}\" style=\"width: 100%;\"\n                        {% if sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"random{{sensor.id}}\">Random:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"random{{sensor.id}}\" \n                        {% if sensor.random: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_min{{sensor.id}}\">Min</label>\n                </div>\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_max{{sensor.id}}\">Max</label>\n                </div>\n            </div>\n            <div class=\"row\">\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"random_min{{sensor.id}}\" value=\"{{sensor.random_min}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"random_max{{sensor.id}}\" value=\"{{sensor.random_max}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n            var is_random_control = document.getElementById(\"random{{sensor.id}}\")\n            var random_min_control = document.getElementById(\"random_min{{sensor.id}}\")\n            var random_max_control = document.getElementById(\"random_max{{sensor.id}}\")\n            var value_control = document.getElementById(\"value{{sensor.id}}\")\n\n            is_random_control.addEventListener(\"change\", function() {\n                value_control.disabled = is_random_control.checked\n                random_min_control.disabled = !is_random_control.checked\n                random_max_control.disabled = !is_random_control.checked\n            })\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var is_random = is_random_control.checked\n                var random_min = parseFloat(random_min_control.value)\n                var random_max = parseFloat(random_max_control.value)\n                var value = parseFloat(value_control.value)\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    'value': value,\n                    'is_random': is_random,\n                    'random_min': random_min,\n                    'random_max': random_max\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./float_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/gps_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Port {{sensor.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            \n            <div class=\"row\">\n                <div class=\"col\">\n                    <label for=\"source{{sensor.id}}\">Source:</label>\n                </div>\n                <div class=\"col\">\n                    <select name=\"source{{sensor.id}}\" id=\"source{{sensor.id}}\" class=\"form-control\">\n                        <option value=\"latlon\"\n                            {% if sensor.value_type.name == \"LATLON\": %}\n                                selected\n                            {% endif %}\n                        >Lat/Lon</option>\n                        <option value=\"nmeasentences\"\n                            {% if sensor.value_type.name == \"NMEA\": %}\n                                selected\n                            {% endif %}\n                        >NMEA</option>\n                        <option value=\"gpxfile\"\n                            {% if sensor.value_type.name == \"GPX\": %}\n                                selected\n                            {% endif %}\n                        >GPX file</option>\n                    </select>\n                </div>\n            </div>\n\n            <div id=\"latlonsettings{{sensor.id}}\"\n                {% if sensor.value_type.name == \"LATLON\": %}\n                    style=\"display:block\"\n                {% else %}\n                    style=\"display:none\"\n                {% endif %}\n                >\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <div class=\"col\">\n                        <label for=\"lat{{sensor.id}}\">Lat:</label>\n                    </div>\n                    <div class=\"col\">\n                        <input class=\"form-control\" type=\"number\" id=\"lat{{sensor.id}}\" value=\"{{sensor.lat}}\" style=\"width: 100%;\"/>\n                    </div>\n                </div>\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <div class=\"col\">\n                        <label for=\"lon{{sensor.id}}\">Lat:</label>\n                    </div>\n                    <div class=\"col\">\n                        <input class=\"form-control\" type=\"number\" id=\"lon{{sensor.id}}\" value=\"{{sensor.lon}}\" style=\"width: 100%;\"/>\n                    </div>\n                </div>\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <div class=\"col\">\n                        <label for=\"number_of_satellites{{sensor.id}}\"># Satellites:</label>\n                    </div>\n                    <div class=\"col\">\n                        <input class=\"form-control\" type=\"number\" id=\"number_of_satellites{{sensor.id}}\" value=\"{{sensor.number_of_satellites}}\" style=\"width: 100%;\"/>\n                    </div>\n                </div>\n            </div>\n\n            <div id=\"nmeasettings{{sensor.id}}\"\n                {% if sensor.value_type.name == \"NMEA\": %}\n                    style=\"display:block\"\n                {% else %}\n                    style=\"display:none\"\n                {% endif %}\n            >\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <div class=\"col\">\n                        <label for=\"nmea{{sensor.id}}\">NMEA:</label>\n                    </div>\n                    <div class=\"col\">\n                        <textarea id=\"nmea{{sensor.id}}\" name=\"nmea{{sensor.id}}\" rows=\"8\" cols=\"100\" style=\"width: 100%;white-space: nowrap;\">{{sensor.raw_nmea}}</textarea>\n                    </div>\n                </div>\n            </div>\n\n            <div id=\"gpxsettings{{sensor.id}}\"\n                {% if sensor.value_type.name == \"GPX\": %}\n                    style=\"display:block\"\n                {% else %}\n                    style=\"display:none\"\n                {% endif %}\n            >\n                <div class=\"row\" style=\"margin-top: 20px;\">\n\n                    <div class=\"col\">\n                        <label for=\"gpx_file_name{{sensor.id}}\">GPX File:</label>\n                    </div>\n                    <div class=\"col\">\n                        {{sensor.gpx_file_name}}\n                    </div>\n                </div>\n                <div class=\"row\" style=\"margin-top: 20px;\">\n                    <input class=\"form-control\" type=\"file\" id=\"gpx_file_name{{sensor.id}}\" value=\"\" style=\"width: 100%;\"/>\n                </div>\n            </div>\n\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"repeat{{sensor.id}}\">Repeat:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"repeat{{sensor.id}}\" \n                        {% if sensor.repeat: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var source_control = document.getElementById(\"source{{sensor.id}}\")\n            var lat_lon_settings_control = document.getElementById(\"latlonsettings{{sensor.id}}\")\n            var nmea_settings_control = document.getElementById(\"nmeasettings{{sensor.id}}\")\n            var gpx_file_settings_control = document.getElementById(\"gpxsettings{{sensor.id}}\")\n\n            var lat_control = document.getElementById(\"lat{{sensor.id}}\")\n            var lon_control = document.getElementById(\"lon{{sensor.id}}\")\n            var number_of_satellites_control = document.getElementById(\"number_of_satellites{{sensor.id}}\")\n            var nmea_control = document.getElementById(\"nmea{{sensor.id}}\")\n            var gpx_file_control = document.getElementById(\"gpx_file_name{{sensor.id}}\")\n            var repeat_control = document.getElementById(\"repeat{{sensor.id}}\")\n\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n\n            source_control.addEventListener(\"change\", function () {\n                var selected_source = source_control.value\n\n                if (selected_source == \"latlon\") {\n                    lat_lon_settings_control.style.display = \"block\"\n                    nmea_settings_control.style.display = \"none\"\n                    gpx_file_settings_control.style.display = \"none\"\n                }\n                else if (selected_source == \"nmeasentences\") {\n                    lat_lon_settings_control.style.display = \"none\"\n                    nmea_settings_control.style.display = \"block\"\n                    gpx_file_settings_control.style.display = \"none\"\n                }\n                else if (selected_source == \"gpxfile\") {\n                    lat_lon_settings_control.style.display = \"none\"\n                    nmea_settings_control.style.display = \"none\"\n                    gpx_file_settings_control.style.display = \"block\"\n                }\n            })\n\n            var post_gps_sensor_settings = function (payload) {\n                var xhr = new XMLHttpRequest();\n                var url = \"./gps_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            }\n\n            set_button.addEventListener(\"click\", function() {\n                var selected_source = source_control.value\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    \"repeat\": repeat_control.checked,\n                    \"source\": selected_source\n                }\n\n                if (selected_source == \"latlon\") {\n                    payload[\"lat\"] = parseFloat(lat_control.value)\n                    payload[\"lon\"] = parseFloat(lon_control.value)\n                    payload[\"number_of_satellites\"] = parseInt(number_of_satellites_control.value)\n                    post_gps_sensor_settings(payload)\n                }\n                else if (selected_source == \"nmeasentences\") {\n                    payload[\"nmea\"] = nmea_control.value\n                    post_gps_sensor_settings(payload)\n                }\n                else if (selected_source == \"gpxfile\") {\n                    const file = gpx_file_control.files[0];\n                    payload[\"gpx_file_name\"] = file.name\n\n                    const reader = new FileReader();\n                    reader.onload = function(event)\n                    {\n                        // NOTE: event.target point to FileReader\n                        var contents = event.target.result;\n                        payload[\"gpx\"] = contents;\n                        post_gps_sensor_settings(payload)\n                    };\n                    \n                    reader.readAsText(file);\n                }\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/home.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <title>CounterFit</title>\n    <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='style.css') }}\">\n\n    <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css\" rel=\"stylesheet\"\n          integrity=\"sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x\" crossorigin=\"anonymous\">\n    <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js\"\n            integrity=\"sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4\" crossorigin=\"anonymous\"></script>\n\n    <link rel=\"icon\" type=\"image/png\" href=\"{{ url_for('static', filename='images/favicon.png') }}\">\n\n    <script src=\"{{ url_for('static', filename='socket.io.min.js') }}\"></script>\n    <script type=\"text/javascript\" charset=\"utf-8\">\n        var socket = io();\n        socket.on('connect', function () {\n            socket.emit('my event', { data: 'I\\'m connected!' });\n        });\n        socket.onmessage = (data) => {\n            console.log(data);\n        };\n        socket.on('device_connect', function(data) {\n            var connection_led_title = document.getElementById(\"connectionLedTitleId\")\n            var connection_led = document.getElementById(\"connectionLed\")\n            var connection_label = document.getElementById(\"connectionLabel\")\n\n            if (data.connected) {\n                connection_led_title.innerHTML = \"Connected\"\n                connection_label.innerHTML = \"Connected\"\n                connection_label.style.color = \"white\"\n                connection_led.style.visibility = \"visible\"\n            }  else {\n                connection_led_title.innerHTML = \"Disconnected\"\n                connection_label.innerHTML = \"Disconnected\"\n                connection_label.style.color = \"grey\"\n                connection_led.style.visibility = \"hidden\"\n            }\n        });\n    </script>\n</head>\n\n<body class=\"bg-light\" style=\"background-color: grey!important;\">\n    <header class=\"bd-header bg-dark py-3 d-flex align-items-stretch border-bottom border-dark fixed-top\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h1 class=\"d-flex align-items-center fs-4 text-white mb-0\">\n                <img src=\"{{ url_for('static', filename='images/CounterFitLogo.png') }}\" width=\"58\" height=\"58\"\n                    class=\"me-3\" alt=\"CounterFit logo\">\n                CounterFit - Fake IoT Hardware\n            </h1>\n\n            <div class=\"ms-auto\">\n                <span id=\"connectionLabel\"\n                {% if is_connected: %}\n                      style=\"color:white;\">Connected\n                {% else %}\n                      style=\"color:grey;\">Disconnected\n                {% endif %}\n                </span>\n                <svg width=\"24px\" height=\"40px\" viewBox=\"0 0 48 80\" version=\"1.1\" \n                        xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n                        role=\"img\" aria-labelledby=\"connectionLedTitleId\" style=\"margin-left: 20px;\">\n                    <title id=\"connectionLedTitleId\">Disconnected</title>\n                    <g id=\"Symbols\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n                        <g id=\"Artboard\" transform=\"translate(0.000000, 2.000000)\" fill-rule=\"nonzero\">\n                            <g id=\"LED\" transform=\"translate(0.353000, -0.353000)\">\n                                <rect id=\"Base\" x=\"0\" y=\"43.445\" width=\"46.86\" height=\"8.059\" fill=\"#FFFFFF\" stroke=\"#000000\" stroke-width=\"1\"></rect>\n                                <g id=\"Pins\" transform=\"translate(6.209000, 47.009000)\" fill=\"#FFFFFF\" stroke=\"#000000\" stroke-width=\"1\">\n                                    <rect id=\"Rectangle2\" x=\"0\" y=\"8.8817842e-15\" width=\"6\" height=\"30.557\"></rect>\n                                    <rect id=\"Rectangle1\" x=\"28.442\" y=\"8.8817842e-15\" width=\"6\" height=\"30.557\"></rect>\n                                </g>\n                                <path d=\"M23.43,0 C11.844,0 2.452,9.392 2.452,20.978 L2.452,42.941 L44.407,42.941 L44.407,20.978 C44.408,9.392 35.016,0 23.43,0 Z M40.155,20.791 C40.126,21.09 40.098,21.383 40.071,21.668 C40.011,22.237 39.934,22.776 39.861,23.272 C39.723,24.266 39.553,25.094 39.423,25.673 C39.291,26.253 39.19,26.584 39.19,26.584 C39.19,26.584 39.087,26.253 38.957,25.673 C38.827,25.093 38.655,24.265 38.518,23.272 C38.445,22.775 38.368,22.237 38.308,21.668 C38.281,21.384 38.252,21.092 38.222,20.793 C38.174,20.511 38.141,20.225 38.1,19.934 C38.065,19.645 38.03,19.35 37.994,19.052 C37.952,18.756 37.864,18.462 37.811,18.163 C37.749,17.864 37.682,17.565 37.63,17.262 C37.57,16.96 37.455,16.671 37.372,16.373 L37.123,15.482 C37.025,15.191 36.906,14.908 36.8,14.623 C36.689,14.339 36.586,14.054 36.487,13.769 C36.369,13.495 36.228,13.234 36.11,12.966 C35.989,12.698 35.87,12.435 35.753,12.176 C35.635,11.918 35.477,11.69 35.353,11.448 C35.081,10.982 34.872,10.486 34.597,10.11 C34.332,9.719 34.107,9.335 33.899,8.989 C33.658,8.656 33.447,8.365 33.274,8.127 C32.93,7.642 32.753,7.345 32.753,7.345 C32.753,7.345 33.067,7.491 33.585,7.783 C33.84,7.933 34.163,8.097 34.501,8.331 C34.831,8.587 35.211,8.882 35.608,9.234 C36.037,9.566 36.391,9.996 36.795,10.444 C36.991,10.672 37.203,10.901 37.39,11.156 C37.565,11.42 37.743,11.689 37.924,11.963 C38.104,12.24 38.291,12.521 38.456,12.818 C38.604,13.123 38.754,13.434 38.896,13.751 C39.033,14.07 39.201,14.382 39.314,14.714 L39.625,15.727 C39.715,16.069 39.834,16.404 39.896,16.75 C39.958,17.097 40.002,17.445 40.053,17.79 C40.098,18.134 40.163,18.474 40.172,18.814 C40.173,19.153 40.174,19.49 40.175,19.82 C40.166,20.154 40.168,20.476 40.155,20.791 Z\"\n                                    stroke=\"#000000\" stroke-width=\"2\" fill=\"#FFFFFF\">\n                                </path>\n                                <path id=\"connectionLed\" d=\"M23.43,0 C11.844,0 2.452,9.392 2.452,20.978 L2.452,42.941 L44.407,42.941 L44.407,20.978 C44.408,9.392 35.016,0 23.43,0 Z M40.155,20.791 C40.126,21.09 40.098,21.383 40.071,21.668 C40.011,22.237 39.934,22.776 39.861,23.272 C39.723,24.266 39.553,25.094 39.423,25.673 C39.291,26.253 39.19,26.584 39.19,26.584 C39.19,26.584 39.087,26.253 38.957,25.673 C38.827,25.093 38.655,24.265 38.518,23.272 C38.445,22.775 38.368,22.237 38.308,21.668 C38.281,21.384 38.252,21.092 38.222,20.793 C38.174,20.511 38.141,20.225 38.1,19.934 C38.065,19.645 38.03,19.35 37.994,19.052 C37.952,18.756 37.864,18.462 37.811,18.163 C37.749,17.864 37.682,17.565 37.63,17.262 C37.57,16.96 37.455,16.671 37.372,16.373 L37.123,15.482 C37.025,15.191 36.906,14.908 36.8,14.623 C36.689,14.339 36.586,14.054 36.487,13.769 C36.369,13.495 36.228,13.234 36.11,12.966 C35.989,12.698 35.87,12.435 35.753,12.176 C35.635,11.918 35.477,11.69 35.353,11.448 C35.081,10.982 34.872,10.486 34.597,10.11 C34.332,9.719 34.107,9.335 33.899,8.989 C33.658,8.656 33.447,8.365 33.274,8.127 C32.93,7.642 32.753,7.345 32.753,7.345 C32.753,7.345 33.067,7.491 33.585,7.783 C33.84,7.933 34.163,8.097 34.501,8.331 C34.831,8.587 35.211,8.882 35.608,9.234 C36.037,9.566 36.391,9.996 36.795,10.444 C36.991,10.672 37.203,10.901 37.39,11.156 C37.565,11.42 37.743,11.689 37.924,11.963 C38.104,12.24 38.291,12.521 38.456,12.818 C38.604,13.123 38.754,13.434 38.896,13.751 C39.033,14.07 39.201,14.382 39.314,14.714 L39.625,15.727 C39.715,16.069 39.834,16.404 39.896,16.75 C39.958,17.097 40.002,17.445 40.053,17.79 C40.098,18.134 40.163,18.474 40.172,18.814 C40.173,19.153 40.174,19.49 40.175,19.82 C40.166,20.154 40.168,20.476 40.155,20.791 Z\"\n                                    stroke=\"#000000\" stroke-width=\"2\" fill=\"#F98500\"\n                                    {% if is_connected: %}\n                                        visibility=\"visible\"\n                                    {% else %}\n                                        visibility=\"hidden\"\n                                    {% endif %}\n                                    >\n                                </path>\n                            </g>\n                        </g>\n                    </g>\n                </svg>\n            </div>\n        </div>\n    </header>\n\n    <div class=\"card shadow-sm border-dark\" style=\"margin-left: 10px;margin-right: 10px; margin-bottom: 10px; margin-top: 100px;\">\n        <div class=\"card-header py-3\">\n            <h2 style=\"text-align:center\">Sensors</h2>\n        </div>\n        <div class=\"card-body\">\n            <div class=\"container-fluid\">\n                <div class=\"row\">\n                    <div class=\"col-3\">\n                        <div class=\"card shadow-sm border-primary\" style=\"margin-top: 11px;\">\n                            <div class=\"card-header py-3 text-white bg-primary\">\n                                <h4 class=\"my-0 fw-normal\" style=\"text-align:center\">Create sensor</h4>\n                            </div>\n                            <div class=\"card-body\">\n                                <div class=\"container\">\n                                    <div class=\"row\">\n                                      <div class=\"col\">\n                                        <label for=\"new_sensor_type\">Sensor Type:</label>\n                                      </div>\n                                      <div class=\"col\">\n                                        <select name=\"new_sensor_type\" id=\"new_sensor_type\" class=\"form-control\">\n                                            {% for sensor_type in all_sensors %}\n                                            <option value=\"{{ sensor_type.sensor_name() }}\" data-sensortype=\"{{ sensor_type.sensor_type() }}\">{{ sensor_type.sensor_name() }}</option>\n                                            {% endfor %}\n                                        </select>\n                                      </div>\n                                    </div>\n                                    \n                                    <div id=\"pin_sensor_create_settings\" style=\"display:block\">\n                                        {% include \"pin_sensor_create_settings.html\" %}\n                                    </div>\n                                    \n                                    <div id=\"serial_sensor_create_settings\" style=\"display:none\">\n                                        {% include \"serial_sensor_create_settings.html\" %}\n                                    </div>\n                                    \n                                    <div id=\"binary_sensor_create_settings\" style=\"display:none\">\n                                        {% include \"binary_sensor_create_settings.html\" %}\n                                    </div>\n                                    \n                                    <div id=\"i2c_sensor_create_settings\" style=\"display:none\">\n                                        {% include \"i2c_sensor_create_settings.html\" %}\n                                    </div>\n\n                                </div>\n                            </div>\n                            <div class=\"card-footer\">\n                                <button type=\"button\" id=\"add_new_sensor_button\" class=\"w-100 btn btn-lg btn-primary\">Add</button>\n                            </div>\n                        </div>\n                    </div>\n                    <div class=\"col-9\">\n                        <div class=\"container\">\n                            <div class=\"row row-cols-1 row-cols-sm-2 row-cols-sm-3 g-3\">\n                                \n                                {% for sensor in sensors %}\n            \n                                    <div class=\"col\">\n                                        {% if sensor.sensor_type().name == \"FLOAT\": %}\n                                            {% with sensor=sensor %}\n                                                {% include \"float_sensor.html\" %}\n                                            {% endwith %}\n                                        {% endif %}\n                                        {% if sensor.sensor_type().name == \"INTEGER\": %}\n                                            {% with sensor=sensor %}\n                                                {% include \"integer_sensor.html\" %}\n                                            {% endwith %}\n                                        {% endif %}\n                                        {% if sensor.sensor_type().name == \"BOOLEAN\": %}\n                                            {% with sensor=sensor %}\n                                                {% include \"boolean_sensor.html\" %}\n                                            {% endwith %}\n                                        {% endif %}\n                                        {% if sensor.sensor_type().name == \"I2C\": %}\n                                            {% if sensor.sensor_unit_type().name == \"FLOAT\" %}\n                                                {% with sensor=sensor %}\n                                                    {% include \"i2c_float_sensor.html\" %}\n                                                {% endwith %}\n                                            {% endif %}\n                                            {% if sensor.sensor_unit_type().name == \"INTEGER\" %}\n                                                {% with sensor=sensor %}\n                                                    {% include \"i2c_integer_sensor.html\" %}\n                                                {% endwith %}\n                                            {% endif %}\n                                        {% endif %}\n                                        {% if sensor.sensor_name() == \"UART GPS\": %}\n                                            {% with sensor=sensor %}\n                                                {% include \"gps_sensor.html\" %}\n                                            {% endwith %}\n                                        {% endif %}\n                                        {% if sensor.sensor_name() == \"Camera\": %}\n                                            {% with sensor=sensor %}\n                                                {% include \"camera_sensor.html\" %}\n                                            {% endwith %}\n                                        {% endif %}\n                                    </div>\n            \n                                {% endfor %}\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <div class=\"card shadow-sm border-dark\" style=\"margin: 10px;\">\n        <div class=\"card-header py-3\">\n            <h2 style=\"text-align:center\">Actuators</h2>\n        </div>\n        <div class=\"card-body\">\n            <div class=\"container-fluid\">\n                <div class=\"row\">\n                    <div class=\"col-3\">\n                        <div class=\"card shadow-sm  border-primary\" style=\"margin-top: 11px;\">\n                            <div class=\"card-header py-3 text-white bg-primary\">\n                                <h4 class=\"my-0 fw-normal\" style=\"text-align:center\">Create actuator</h4>\n                            </div>\n                            <div class=\"card-body\">\n                                <div class=\"container\">\n                                    <div class=\"row\">\n                                      <div class=\"col\">\n                                        <label for=\"new_actuator_type\">Actuator Type:</label>\n                                      </div>\n                                      <div class=\"col\">\n                                        <select name=\"new_actuator_type\" id=\"new_actuator_type\" class=\"form-control\">\n                                            {% for actuator_type in all_actuators %}\n                                            <option value=\"{{ actuator_type.actuator_name() }}\">{{ actuator_type.actuator_name() }}</option>\n                                            {% endfor %}\n                                        </select>\n                                      </div>\n                                    </div>\n                                    <div class=\"row\" style=\"margin-top: 20px;\">\n                                      <div class=\"col\">\n                                        <label for=\"new_actuator_port\">Pin:</label>\n                                      </div>\n                                      <div class=\"col\">\n                                        <select name=\"new_actuator_port\" id=\"new_actuator_port\" class=\"form-control\">\n                                            {% for port in ports %}\n                                            <option value=\"{{ port }}\">{{ port }}</option>\n                                            {% endfor %}\n                                        </select>\n                                      </div>\n                                    </div>\n                                </div>\n                            </div>\n                            <div class=\"card-footer\">\n                                <button type=\"button\" id=\"add_new_actuator_button\" class=\"w-100 btn btn-lg btn-primary\" >Add</button>\n                            </div>\n                        </div>\n                    </div>\n                    <div class=\"col-9\">\n                        <div class=\"container\">\n                            <div class=\"row row-cols-1 row-cols-sm-2 row-cols-sm-3 g-3\">\n                                \n                                {% for actuator in actuators %}\n\n                                <div class=\"col\">\n                                    {% if actuator.actuator_name() == \"LED\": %}\n                                        {% with actuator=actuator %}\n                                            {% include \"led_actuator.html\" %}\n                                        {% endwith %}\n                                    {% elif actuator.actuator_name() == \"Relay\": %}\n                                        {% with actuator=actuator %}\n                                            {% include \"relay_actuator.html\" %}\n                                        {% endwith %}\n                                    {% endif %}\n                                </div>\n\n                                {% endfor %}\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var add_new_sensor_button = document.getElementById(\"add_new_sensor_button\")\n            var add_new_actuator_button = document.getElementById(\"add_new_actuator_button\")\n            var new_sensor_type_control = document.getElementById(\"new_sensor_type\")\n            var new_actuator_type_control = document.getElementById(\"new_actuator_type\")\n            var new_sensor_pin_control = document.getElementById(\"new_sensor_pin\")\n            var new_i2c_sensor_pin_control = document.getElementById(\"new_i2c_sensor_pin\")\n            var new_sensor_port_control = document.getElementById(\"new_sensor_port\")\n            var new_sensor_name_control = document.getElementById(\"new_sensor_name\")\n            var new_actuator_port_control = document.getElementById(\"new_actuator_port\")\n            var new_sensor_units_control = document.getElementById(\"new_sensor_units\")\n            var new_i2c_sensor_units_control = document.getElementById(\"new_i2c_sensor_units\")            \n            var pin_sensor_create_settings = document.getElementById(\"pin_sensor_create_settings\")\n            var serial_sensor_create_settings = document.getElementById(\"serial_sensor_create_settings\")\n            var binary_sensor_create_settings = document.getElementById(\"binary_sensor_create_settings\")\n            var i2c_sensor_create_settings = document.getElementById(\"i2c_sensor_create_settings\")\n\n            new_sensor_type_control.addEventListener(\"change\", function () {\n                var option = new_sensor_type_control.options[new_sensor_type_control.selectedIndex]\n\n                sensor_type = option.getAttribute('data-sensortype')\n\n                if (sensor_type == \"SensorType.SERIAL\"){\n                    pin_sensor_create_settings.style.display = \"none\"\n                    serial_sensor_create_settings.style.display = \"block\"\n                    binary_sensor_create_settings.style.display = \"none\"\n                    i2c_sensor_create_settings.style.display = \"none\"\n                }\n                else if (sensor_type == \"SensorType.BINARY\"){\n                    pin_sensor_create_settings.style.display = \"none\"\n                    serial_sensor_create_settings.style.display = \"none\"\n                    binary_sensor_create_settings.style.display = \"block\"\n                    i2c_sensor_create_settings.style.display = \"none\"\n                }\n                else if (sensor_type == \"SensorType.I2C\"){\n                    pin_sensor_create_settings.style.display = \"none\"\n                    serial_sensor_create_settings.style.display = \"none\"\n                    binary_sensor_create_settings.style.display = \"none\"\n                    i2c_sensor_create_settings.style.display = \"block\"\n                }\n                else {\n                    pin_sensor_create_settings.style.display = \"block\"\n                    serial_sensor_create_settings.style.display = \"none\"\n                    binary_sensor_create_settings.style.display = \"none\"\n                    i2c_sensor_create_settings.style.display = \"none\"\n                }\n            })\n\n            add_new_sensor_button.addEventListener(\"click\", function () {\n                var new_sensor_type = new_sensor_type_control.value\n                var new_sensor_unit = new_sensor_units_control.value\n                var new_i2c_sensor_unit = new_i2c_sensor_units_control.value\n                var new_sensor_pin = parseInt(new_sensor_pin_control.value)\n                var new_i2c_sensor_pin = parseInt(new_i2c_sensor_pin_control.value)\n                var new_sensor_port = new_sensor_port_control.value\n                var new_sensor_name = new_sensor_name_control.value\n\n                var payload = {\n                    'type': new_sensor_type,\n                    'pin': new_sensor_pin,\n                    'i2c_pin': new_i2c_sensor_pin,\n                    'port': new_sensor_port,\n                    'name': new_sensor_name,\n                    'unit': new_sensor_unit,\n                    'i2c_unit': new_i2c_sensor_unit\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./create_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n\n            add_new_actuator_button.addEventListener(\"click\", function () {\n                var new_actuator_type = new_actuator_type_control.value\n                var new_actuator_port = parseInt(new_actuator_port_control.value)\n\n                var payload = {\n                    'type': new_actuator_type,\n                    'port': new_actuator_port\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./create_actuator\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n\n            function populate_units() {\n                var new_sensor_type = new_sensor_type_control.value\n\n                var payload = {\n                    'type': new_sensor_type,\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./sensor_units\";\n\n                xhr.onreadystatechange = function () {\n                    if (xhr.readyState === 4) {\n                        response = JSON.parse(xhr.response)\n                        var inner = \"\"\n                        response.units.forEach(function (item, index) {\n                            inner += \"<option value=\\\"\" + item + \"\\\">\" + item + \"</option>\"\n                        });\n\n                        new_sensor_units_control.innerHTML = inner\n                        new_i2c_sensor_units_control.innerHTML = inner\n                    }\n                }\n\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            }\n\n            new_sensor_type_control.addEventListener('change', (event) => {\n                populate_units()\n            });\n\n            populate_units()\n        })\n    </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/i2c_float_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">I<sup>2</sup>C: {{sensor.address}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    Units:\n                </div>\n                <div class=\"col\">\n                    {{ sensor.unit }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    Value range:\n                </div>\n                <div class=\"col\">\n                    {{ \"{:,.2f}\".format(sensor.valid_min) }} to {{ \"{:,.2f}\".format(sensor.valid_max) }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"value{{sensor.id}}\">Value:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"value{{sensor.id}}\" value=\"{{sensor.value}}\" style=\"width: 100%;\"\n                    {% if sensor.random: %}\n                        disabled\n                    {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"random{{sensor.id}}\">Random:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"random{{sensor.id}}\" \n                        {% if sensor.random: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_min{{sensor.id}}\">Min</label>\n                </div>\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_max{{sensor.id}}\">Max</label>\n                </div>\n            </div>\n            <div class=\"row\">\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"random_min{{sensor.id}}\" value=\"{{sensor.random_min}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" id=\"random_max{{sensor.id}}\" value=\"{{sensor.random_max}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n            var is_random_control = document.getElementById(\"random{{sensor.id}}\")\n            var random_min_control = document.getElementById(\"random_min{{sensor.id}}\")\n            var random_max_control = document.getElementById(\"random_max{{sensor.id}}\")\n            var value_control = document.getElementById(\"value{{sensor.id}}\")\n\n            is_random_control.addEventListener(\"change\", function() {\n                value_control.disabled = is_random_control.checked\n                random_min_control.disabled = !is_random_control.checked\n                random_max_control.disabled = !is_random_control.checked\n            })\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var is_random = is_random_control.checked\n                var random_min = parseFloat(random_min_control.value)\n                var random_max = parseFloat(random_max_control.value)\n                var value = parseFloat(value_control.value)\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    'value': value,\n                    'is_random': is_random,\n                    'random_min': random_min,\n                    'random_max': random_max\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./float_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/i2c_integer_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">I<sup>2</sup>C: {{sensor.address}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    Units:\n                </div>\n                <div class=\"col\">\n                    {{ sensor.unit }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    Value range:\n                </div>\n                <div class=\"col\">\n                    {{ \"{:,d}\".format(sensor.valid_min) }} to {{ \"{:,d}\".format(sensor.valid_max) }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"value{{sensor.id}}\">Value:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"value{{sensor.id}}\" value=\"{{sensor.value}}\" style=\"width: 100%;\"\n                        {% if sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"random{{sensor.id}}\">Random:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"random{{sensor.id}}\" \n                        {% if sensor.random: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_min{{sensor.id}}\">Min</label>\n                </div>\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_max{{sensor.id}}\">Max</label>\n                </div>\n            </div>\n            <div class=\"row\">\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"random_min{{sensor.id}}\" value=\"{{sensor.random_min}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"random_max{{sensor.id}}\" value=\"{{sensor.random_max}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n            var is_random_control = document.getElementById(\"random{{sensor.id}}\")\n            var random_min_control = document.getElementById(\"random_min{{sensor.id}}\")\n            var random_max_control = document.getElementById(\"random_max{{sensor.id}}\")\n            var value_control = document.getElementById(\"value{{sensor.id}}\")\n\n            is_random_control.addEventListener(\"change\", function() {\n                value_control.disabled = is_random_control.checked\n                random_min_control.disabled = !is_random_control.checked\n                random_max_control.disabled = !is_random_control.checked\n            })\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var is_random = is_random_control.checked\n                var random_min = parseInt(random_min_control.value)\n                var random_max = parseInt(random_max_control.value)\n                var value = parseInt(value_control.value)\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    'value': value,\n                    'is_random': is_random,\n                    'random_min': random_min,\n                    'random_max': random_max\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./integer_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/i2c_sensor_create_settings.html",
    "content": "<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_i2c_sensor_units\">Units:</label>\n    </div>\n    <div class=\"col\">\n        <select name=\"new_i2c_sensor_units\" id=\"new_i2c_sensor_units\" class=\"form-control\">\n        </select>\n    </div>\n</div>\n<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_i2c_sensor_pin\">I<sup>2</sup>C address:</label>\n    </div>\n    <div class=\"col\">\n        <select name=\"new_i2c_sensor_pin\" id=\"new_i2c_sensor_pin\" class=\"form-control\">\n            {% for port in ports_and__hex %}\n            <option value=\"{{ port[0] }}\">{{ port[1] }}</option>\n            {% endfor %}\n        </select>\n    </div>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/integer_sensor.html",
    "content": "<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ sensor.sensor_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Pin {{sensor.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    Units:\n                </div>\n                <div class=\"col\">\n                    {{ sensor.unit }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    Value range:\n                </div>\n                <div class=\"col\">\n                    {{ \"{:,d}\".format(sensor.valid_min) }} to {{ \"{:,d}\".format(sensor.valid_max) }}\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"value{{sensor.id}}\">Value:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"value{{sensor.id}}\" value=\"{{sensor.value}}\" style=\"width: 100%;\"\n                        {% if sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    <label for=\"random{{sensor.id}}\">Random:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-check-input\" type=\"checkbox\" id=\"random{{sensor.id}}\" \n                        {% if sensor.random: %}\n                            checked\n                        {% endif %}\n                    />\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_min{{sensor.id}}\">Min</label>\n                </div>\n                <div class=\"col\" style=\"text-align:center\">\n                    <label for=\"random_max{{sensor.id}}\">Max</label>\n                </div>\n            </div>\n            <div class=\"row\">\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"random_min{{sensor.id}}\" value=\"{{sensor.random_min}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"number\" step=\"1\" pattern=\"\\d+\" id=\"random_max{{sensor.id}}\" value=\"{{sensor.random_max}}\" style=\"width: 100%;\"\n                        {% if not sensor.random: %}\n                            disabled\n                        {% endif %}\n                    />\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"sensor_set_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"sensor_delete_button{{sensor.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"sensor_set_button{{sensor.id}}\")\n            var delete_button = document.getElementById(\"sensor_delete_button{{sensor.id}}\")\n            var is_random_control = document.getElementById(\"random{{sensor.id}}\")\n            var random_min_control = document.getElementById(\"random_min{{sensor.id}}\")\n            var random_max_control = document.getElementById(\"random_max{{sensor.id}}\")\n            var value_control = document.getElementById(\"value{{sensor.id}}\")\n\n            is_random_control.addEventListener(\"change\", function() {\n                value_control.disabled = is_random_control.checked\n                random_min_control.disabled = !is_random_control.checked\n                random_max_control.disabled = !is_random_control.checked\n            })\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var is_random = is_random_control.checked\n                var random_min = parseInt(random_min_control.value)\n                var random_max = parseInt(random_max_control.value)\n                var value = parseInt(value_control.value)\n\n                var payload = {\n                    \"port\": \"{{sensor.port}}\",\n                    'value': value,\n                    'is_random': is_random,\n                    'random_min': random_min,\n                    'random_max': random_max\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./integer_sensor_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{sensor.port}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_sensor\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n        })\n    </script>\n</div>\n"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/led_actuator.html",
    "content": "\n<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ actuator.actuator_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Pin {{actuator.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <label for=\"color{{actuator.id}}\">Color:</label>\n                </div>\n                <div class=\"col\">\n                    <input class=\"form-control\" type=\"color\" id=\"color{{actuator.id}}\" value=\"{{actuator.color}}\" style=\"width: 100%;\"/>\n                </div>\n            </div>\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    \n                </div>\n                <div class=\"col\">\n                    <svg class=\"mx-auto d-block\" width=\"48px\" height=\"80px\" viewBox=\"0 0 48 80\" version=\"1.1\" \n                         xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n                         role=\"img\" aria-labelledby=\"ledTitleId{{actuator.id}}\">\n                        <title id=\"ledTitleId{{actuator.id}}\">{% if actuator.value: %}LED on{% else %}LED off{% endif %}</title>\n                        <g id=\"Symbols\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n                            <g id=\"Artboard\" transform=\"translate(0.000000, 2.000000)\" fill-rule=\"nonzero\">\n                                <g id=\"LED\" transform=\"translate(0.353000, -0.353000)\">\n                                    <rect id=\"Base\" fill=\"#000000\" x=\"0\" y=\"43.445\" width=\"46.86\" height=\"8.059\"></rect>\n                                    <g id=\"Pins\" transform=\"translate(6.209000, 47.009000)\" fill=\"#000000\">\n                                        <rect id=\"Rectangle2\" x=\"0\" y=\"8.8817842e-15\" width=\"6\" height=\"30.557\"></rect>\n                                        <rect id=\"Rectangle1\" x=\"28.442\" y=\"8.8817842e-15\" width=\"6\" height=\"30.557\"></rect>\n                                    </g>\n                                    <path d=\"M23.43,0 C11.844,0 2.452,9.392 2.452,20.978 L2.452,42.941 L44.407,42.941 L44.407,20.978 C44.408,9.392 35.016,0 23.43,0 Z M40.155,20.791 C40.126,21.09 40.098,21.383 40.071,21.668 C40.011,22.237 39.934,22.776 39.861,23.272 C39.723,24.266 39.553,25.094 39.423,25.673 C39.291,26.253 39.19,26.584 39.19,26.584 C39.19,26.584 39.087,26.253 38.957,25.673 C38.827,25.093 38.655,24.265 38.518,23.272 C38.445,22.775 38.368,22.237 38.308,21.668 C38.281,21.384 38.252,21.092 38.222,20.793 C38.174,20.511 38.141,20.225 38.1,19.934 C38.065,19.645 38.03,19.35 37.994,19.052 C37.952,18.756 37.864,18.462 37.811,18.163 C37.749,17.864 37.682,17.565 37.63,17.262 C37.57,16.96 37.455,16.671 37.372,16.373 L37.123,15.482 C37.025,15.191 36.906,14.908 36.8,14.623 C36.689,14.339 36.586,14.054 36.487,13.769 C36.369,13.495 36.228,13.234 36.11,12.966 C35.989,12.698 35.87,12.435 35.753,12.176 C35.635,11.918 35.477,11.69 35.353,11.448 C35.081,10.982 34.872,10.486 34.597,10.11 C34.332,9.719 34.107,9.335 33.899,8.989 C33.658,8.656 33.447,8.365 33.274,8.127 C32.93,7.642 32.753,7.345 32.753,7.345 C32.753,7.345 33.067,7.491 33.585,7.783 C33.84,7.933 34.163,8.097 34.501,8.331 C34.831,8.587 35.211,8.882 35.608,9.234 C36.037,9.566 36.391,9.996 36.795,10.444 C36.991,10.672 37.203,10.901 37.39,11.156 C37.565,11.42 37.743,11.689 37.924,11.963 C38.104,12.24 38.291,12.521 38.456,12.818 C38.604,13.123 38.754,13.434 38.896,13.751 C39.033,14.07 39.201,14.382 39.314,14.714 L39.625,15.727 C39.715,16.069 39.834,16.404 39.896,16.75 C39.958,17.097 40.002,17.445 40.053,17.79 C40.098,18.134 40.163,18.474 40.172,18.814 C40.173,19.153 40.174,19.49 40.175,19.82 C40.166,20.154 40.168,20.476 40.155,20.791 Z\"\n                                        stroke=\"#000000\" stroke-width=\"2\" fill=\"#FFFFFF\">\n                                    </path>\n                                    <path id=\"led{{actuator.id}}\" d=\"M23.43,0 C11.844,0 2.452,9.392 2.452,20.978 L2.452,42.941 L44.407,42.941 L44.407,20.978 C44.408,9.392 35.016,0 23.43,0 Z M40.155,20.791 C40.126,21.09 40.098,21.383 40.071,21.668 C40.011,22.237 39.934,22.776 39.861,23.272 C39.723,24.266 39.553,25.094 39.423,25.673 C39.291,26.253 39.19,26.584 39.19,26.584 C39.19,26.584 39.087,26.253 38.957,25.673 C38.827,25.093 38.655,24.265 38.518,23.272 C38.445,22.775 38.368,22.237 38.308,21.668 C38.281,21.384 38.252,21.092 38.222,20.793 C38.174,20.511 38.141,20.225 38.1,19.934 C38.065,19.645 38.03,19.35 37.994,19.052 C37.952,18.756 37.864,18.462 37.811,18.163 C37.749,17.864 37.682,17.565 37.63,17.262 C37.57,16.96 37.455,16.671 37.372,16.373 L37.123,15.482 C37.025,15.191 36.906,14.908 36.8,14.623 C36.689,14.339 36.586,14.054 36.487,13.769 C36.369,13.495 36.228,13.234 36.11,12.966 C35.989,12.698 35.87,12.435 35.753,12.176 C35.635,11.918 35.477,11.69 35.353,11.448 C35.081,10.982 34.872,10.486 34.597,10.11 C34.332,9.719 34.107,9.335 33.899,8.989 C33.658,8.656 33.447,8.365 33.274,8.127 C32.93,7.642 32.753,7.345 32.753,7.345 C32.753,7.345 33.067,7.491 33.585,7.783 C33.84,7.933 34.163,8.097 34.501,8.331 C34.831,8.587 35.211,8.882 35.608,9.234 C36.037,9.566 36.391,9.996 36.795,10.444 C36.991,10.672 37.203,10.901 37.39,11.156 C37.565,11.42 37.743,11.689 37.924,11.963 C38.104,12.24 38.291,12.521 38.456,12.818 C38.604,13.123 38.754,13.434 38.896,13.751 C39.033,14.07 39.201,14.382 39.314,14.714 L39.625,15.727 C39.715,16.069 39.834,16.404 39.896,16.75 C39.958,17.097 40.002,17.445 40.053,17.79 C40.098,18.134 40.163,18.474 40.172,18.814 C40.173,19.153 40.174,19.49 40.175,19.82 C40.166,20.154 40.168,20.476 40.155,20.791 Z\"\n                                        stroke=\"#000000\" stroke-width=\"2\" fill=\"{{actuator.color}}\"\n                                        {% if actuator.value: %}\n                                            visibility=\"visible\"\n                                        {% else %}\n                                            visibility=\"hidden\"\n                                        {% endif %}\n                                        >\n                                    </path>\n                                </g>\n                            </g>\n                        </g>\n                    </svg>\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"actuator_set_button{{actuator.id}}\" class=\"w-100 btn btn-lg btn-primary\">Set</button>\n                </div>\n                <div class=\"col\">\n                    <button id=\"actuator_delete_button{{actuator.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n    \n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var set_button = document.getElementById(\"actuator_set_button{{actuator.id}}\")\n            var delete_button = document.getElementById(\"actuator_delete_button{{actuator.id}}\")\n            var color_control = document.getElementById(\"color{{actuator.id}}\")\n            var led_control = document.getElementById(\"led{{actuator.id}}\")\n            var led_title = document.getElementById(\"ledTitleId{{actuator.id}}\")\n\n            set_button.addEventListener(\"click\", function() {\n                // get the values\n                var color = color_control.value\n\n                var payload = {\n                    \"port\": \"{{actuator.id}}\",\n                    'color': color\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./led_actuator_settings\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n\n                led_control.style.fill=color\n            });\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{actuator.id}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_actuator\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n\n            var socket = io();\n            socket.on('actuator_change{{actuator.id}}', function(data) {\n                if (data.value){\n                    led_control.style.visibility = \"visible\"\n                    led_title.innerHTML = \"LED on\"\n                } else {\n                    led_control.style.visibility = \"hidden\"\n                    led_title.innerHTML = \"LED off\"\n                }\n\n                console.log('actuator change received');\n                console.log(data);\n            });\n        })\n    </script>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/pin_sensor_create_settings.html",
    "content": "<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_sensor_units\">Units:</label>\n    </div>\n    <div class=\"col\">\n        <select name=\"new_sensor_units\" id=\"new_sensor_units\" class=\"form-control\">\n        </select>\n    </div>\n</div>\n<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_sensor_pin\">Pin:</label>\n    </div>\n    <div class=\"col\">\n        <select name=\"new_sensor_pin\" id=\"new_sensor_pin\" class=\"form-control\">\n            {% for port in ports %}\n            <option value=\"{{ port }}\">{{ port }}</option>\n            {% endfor %}\n        </select>\n    </div>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/relay_actuator.html",
    "content": "\n<div class=\"card shadow-sm border-secondary\" style=\"margin: 10px;\">\n    <div class=\"card-header py-3 text-white bg-secondary\">\n        <div class=\"container-fluid d-flex align-items-center\">\n            <h4 class=\"my-0 fw-normal\">{{ actuator.actuator_name() }}</h4>\n            <h4 class=\"my-0 fw-normal ms-auto\">Pin {{actuator.port}}</h4>\n        </div>\n    </div>\n    <div class=\"card-body\">\n        <div class=\"container\">\n            <div class=\"row\" style=\"margin-top: 20px;\">\n                <div class=\"col\">\n                    \n                </div>\n                <div class=\"col\">\n                    <svg width=\"100px\" height=\"70px\" viewBox=\"0 0 100 70\" version=\"1.1\"\n                         xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n                        <title id=\"relayTitleId{{actuator.id}}\">Relay off</title>\n                        <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\">\n                            <g id=\"Artboard\" transform=\"translate(-38.000000, -48.000000)\">\n                                <g id=\"relay\" transform=\"translate(38.000000, 48.000000)\">\n                                    <g id=\"Group\" transform=\"translate(0.000000, 8.048780)\" fill=\"#000000\">\n                                        <g id=\"Coil\" transform=\"translate(1.800000, 0.000000)\" stroke=\"#000000\">\n                                            <path d=\"M34.6652288,0 C30.2799974,0.000716979501 26.7251751,7.25114198 26.7249115,16.1951014 C26.7256647,16.5616279 26.732518,16.9279965 26.7454597,17.2935745 L29.6325367,17.2935745 C29.603894,17.0663725 29.5787654,16.8373106 29.5571897,16.606744 C29.527386,16.2468027 29.5062389,15.8839824 29.4938277,15.5196416 C29.4854396,15.2615087 29.4814421,15.0028379 29.4818427,14.7441155 C29.4816482,14.3788099 29.4902217,14.0137125 29.5075312,13.6501907 C29.5251117,13.2868306 29.5513999,12.9255284 29.5862975,12.5676366 C29.6211462,12.2093075 29.6645807,11.854836 29.7164381,11.5055519 C29.7681183,11.156872 29.8281264,10.8137873 29.8962381,10.4775803 C29.9647693,10.1407973 30.0413516,9.81137364 30.1256976,9.49054651 C30.2094893,9.1696531 30.3009306,8.85768848 30.3996787,8.55582108 C30.4988128,8.25459717 30.6051131,7.96388558 30.7181816,7.68477469 C30.8310061,7.405681 30.9504511,7.13850026 31.0760703,6.88423089 C31.2020488,6.62955955 31.3340675,6.38818459 31.4716307,6.16101205 C31.6091423,5.93400684 31.7520193,5.72147579 31.8997261,5.52421584 C32.0470977,5.32657983 32.1991184,5.14441084 32.3552195,4.97839052 C32.5119183,4.81239073 32.672513,4.66285908 32.8363988,4.53035847 C32.9996336,4.39791614 33.1659421,4.28254298 33.3347026,4.18467026 C33.5039207,4.08726214 33.6753733,4.00756254 33.8484169,3.94587066 C34.0207777,3.88389961 34.194515,3.83987576 34.3689811,3.81396317 C34.4932198,3.79566163 34.6177313,3.78655783 34.7422802,3.78666899 C34.9172702,3.78669367 35.0921505,3.80490855 35.266269,3.8412457 C35.4409935,3.87769374 35.6147325,3.93234469 35.7868332,4.0049933 C35.9589921,4.07740267 36.1293023,4.16772825 36.2971238,4.27563067 C36.4644083,4.38353688 36.6290019,4.50877794 36.7902899,4.65088601 C36.9515484,4.79358936 37.109284,4.95298699 37.262907,5.12848288 C37.4169285,5.30306792 37.5666635,5.49358677 37.7115514,5.69932592 C37.8566331,5.90594971 37.9966534,6.12758997 38.1310854,6.36341281 C38.2648204,6.59861858 38.3928375,6.847582 38.514659,7.10937408 C38.6370516,7.37154796 38.7530679,7.64632092 38.8622723,7.93266148 C38.9715218,8.21899024 39.0738219,8.51653383 39.1687884,8.82417501 C39.2630949,9.13182869 39.3499639,9.44910644 39.4290705,9.77482165 C39.5089312,10.0999261 39.5809412,10.4331438 39.6448309,10.7732273 C39.70848,11.1136897 39.7639163,11.4605703 39.8109318,11.8125695 C39.8577037,12.1646656 39.8959949,12.5214138 39.925662,12.8814777 C39.9554657,13.2414186 39.9766127,13.6042385 39.989024,13.968579 C39.9979854,14.2266699 40.0025561,14.4853411 40.0027274,14.7441062 C40.0023539,15.1094713 39.9932097,15.4745688 39.9753293,15.838031 C39.9577849,16.2021533 39.9314965,16.5642164 39.896563,16.9228592 C39.8821906,17.0469795 39.8667781,17.170567 39.8503325,17.2935745 L42.5798697,17.2935745 C42.5950975,16.9281457 42.6042354,16.5617769 42.6072678,16.1950921 C42.6072678,11.8994145 41.7703038,7.77979717 40.2808671,4.74259276 C38.7914304,1.70538835 36.771414,-0.000585681312 34.6652394,0 L34.6652288,0 Z\" id=\"Path\" stroke-width=\"1.00027776\" fill-rule=\"nonzero\" stroke-linejoin=\"round\"></path>\n                                            <path d=\"M48.2653954,9.31541879e-06 C43.8801641,0.000717275729 40.3253419,7.25114219 40.3250783,16.1951014 C40.3258323,16.5616279 40.3326865,16.9279966 40.3456291,17.2935745 L43.2327059,17.2935745 C43.2040632,17.0663725 43.1789345,16.8373106 43.1573589,16.606744 C43.1275552,16.2468027 43.1064081,15.8839824 43.0939969,15.5196416 C43.0856088,15.2615087 43.0816113,15.0028379 43.0820119,14.7441155 C43.0818173,14.3788099 43.0903909,14.0137125 43.1077003,13.6501907 C43.1252808,13.2868306 43.1515691,12.9255284 43.1864666,12.5676366 C43.2213154,12.2093075 43.2647499,11.854836 43.3166072,11.5055519 C43.3682875,11.156872 43.4282956,10.8137873 43.4964073,10.4775803 C43.5649385,10.1407973 43.6415208,9.81137364 43.7258667,9.49054651 C43.8096585,9.1696531 43.9010997,8.85768848 43.9998479,8.55582108 C44.0989819,8.25459717 44.2052822,7.96388558 44.3183508,7.68477469 C44.4311753,7.405681 44.5506203,7.13850026 44.6762394,6.88423089 C44.802218,6.62955955 44.9342366,6.38818459 45.0717999,6.16101205 C45.2093115,5.93400684 45.3521885,5.72147579 45.4998952,5.52421584 C45.6472668,5.32657983 45.7992876,5.14441084 45.9553887,4.97839052 C46.1120875,4.81239073 46.2726821,4.66285908 46.436568,4.53035847 C46.5998027,4.39791614 46.7661112,4.28254298 46.9348718,4.18467026 C47.1040898,4.08726214 47.2755425,4.00756254 47.4485861,3.94587066 C47.6209469,3.88389961 47.7946841,3.83987576 47.9691503,3.81396317 C48.093389,3.79566163 48.2179005,3.78655783 48.3424494,3.78666899 C48.5174393,3.78669367 48.6923197,3.80490855 48.8664382,3.8412457 C49.0411627,3.87769374 49.2149016,3.93234469 49.3870024,4.0049933 C49.5591613,4.07740267 49.7294715,4.16772825 49.897293,4.27563067 C50.0645774,4.38353688 50.2291711,4.50877794 50.3904591,4.65088601 C50.5517176,4.79358936 50.7094532,4.95298699 50.8630761,5.12848288 C51.0170976,5.30306792 51.1668327,5.49358677 51.3117205,5.69932592 C51.4568023,5.90594971 51.5968225,6.12758997 51.7312545,6.36341281 C51.8649895,6.59861858 51.9930067,6.847582 52.1148282,7.10937408 C52.2372208,7.37154796 52.353237,7.64632092 52.4624414,7.93266148 C52.571691,8.21899024 52.6739911,8.51653383 52.7689575,8.82417501 C52.8632641,9.13182869 52.9501331,9.44910644 53.0292396,9.77482165 C53.1091003,10.0999261 53.1811103,10.4331438 53.245,10.7732273 C53.3086492,11.1136897 53.3640854,11.4605703 53.411101,11.8125695 C53.4578729,12.1646656 53.4961641,12.5214138 53.5258312,12.8814777 C53.5556349,13.2414186 53.5767819,13.6042385 53.5891931,13.968579 C53.5981546,14.2266699 53.6027253,14.4853411 53.6028966,14.7441062 C53.6025232,15.1094744 53.593379,15.4745751 53.5754985,15.8380403 C53.5579541,16.2021626 53.5316657,16.5642257 53.4967322,16.9228685 C53.4823598,17.0469888 53.4669473,17.1705763 53.4505017,17.2935745 L56.1800389,17.2935745 C56.1952637,16.9281549 56.2043988,16.561786 56.2074282,16.1951014 C56.2074282,11.8994238 55.3704642,7.77980649 53.8810275,4.74260207 C52.3915908,1.70539766 50.3715744,-0.000576365893 48.2653998,9.31541879e-06 L48.2653954,9.31541879e-06 Z\" id=\"Path\" stroke-width=\"1.00027776\" fill-rule=\"nonzero\" stroke-linejoin=\"round\"></path>\n                                            <path d=\"M61.8783956,9.31541879e-06 C57.4931643,0.000717275729 53.9383421,7.25114219 53.9380786,16.1951014 C53.9388326,16.5616279 53.9456868,16.9279966 53.9586294,17.2935745 L56.8457061,17.2935745 C56.8170634,17.0663725 56.7919348,16.8373106 56.7703591,16.606744 C56.7405554,16.2468027 56.7194084,15.8839824 56.7069971,15.5196416 C56.698609,15.2615087 56.6946115,15.0028379 56.6950121,14.7441155 C56.6948176,14.3788099 56.7033911,14.0137125 56.7207006,13.6501907 C56.7382811,13.2868306 56.7645693,12.9255284 56.7994669,12.5676366 C56.8343156,12.2093075 56.8777501,11.854836 56.9296075,11.5055519 C56.9812877,11.156872 57.0412958,10.8137873 57.1094075,10.4775803 C57.1779387,10.1407973 57.2545211,9.81137364 57.338867,9.49054651 C57.4226588,9.1696531 57.5141,8.85768848 57.6128481,8.55582108 C57.7119822,8.25459717 57.8182825,7.96388558 57.931351,7.68477469 C58.0441755,7.405681 58.1636205,7.13850026 58.2892397,6.88423089 C58.4152182,6.62955955 58.5472369,6.38818459 58.6848001,6.16101205 C58.8223117,5.93400684 58.9651888,5.72147579 59.1128955,5.52421584 C59.2602671,5.32657983 59.4122879,5.14441084 59.5683889,4.97839052 C59.7250878,4.81239073 59.8856824,4.66285908 60.0495683,4.53035847 C60.212803,4.39791614 60.3791115,4.28254298 60.5478721,4.18467026 C60.7170901,4.08726214 60.8885427,4.00756254 61.0615863,3.94587066 C61.2339472,3.88389961 61.4076844,3.83987576 61.5821505,3.81396317 C61.7063892,3.79566163 61.8309007,3.78655783 61.9554497,3.78666899 C62.1304396,3.78669367 62.30532,3.80490855 62.4794384,3.8412457 C62.654163,3.87769374 62.8279019,3.93234469 63.0000026,4.0049933 C63.1721615,4.07740267 63.3424717,4.16772825 63.5102932,4.27563067 C63.6775777,4.38353688 63.8421713,4.50877794 64.0034593,4.65088601 C64.1647178,4.79358936 64.3224535,4.95298699 64.4760764,5.12848288 C64.6300979,5.30306792 64.7798329,5.49358677 64.9247208,5.69932592 C65.0698025,5.90594971 65.2098228,6.12758997 65.3442548,6.36341281 C65.4779898,6.59861858 65.6060069,6.847582 65.7278284,7.10937408 C65.850221,7.37154796 65.9662373,7.64632092 66.0754417,7.93266148 C66.1846912,8.21899024 66.2869913,8.51653383 66.3819578,8.82417501 C66.4762644,9.13182869 66.5631334,9.44910644 66.6422399,9.77482165 C66.7221006,10.0999261 66.7941106,10.4331438 66.8580003,10.7732273 C66.9216495,11.1136897 66.9770857,11.4605703 67.0241013,11.8125695 C67.0708731,12.1646656 67.1091643,12.5214138 67.1388314,12.8814777 C67.1686351,13.2414186 67.1897822,13.6042385 67.2021934,13.968579 C67.2111549,14.2266699 67.2157255,14.4853411 67.2158968,14.7441062 C67.2155234,15.1094744 67.2063793,15.4745751 67.1884987,15.8380403 C67.1709543,16.2021626 67.144666,16.5642257 67.1097324,16.9228685 C67.0953601,17.0469888 67.0799475,17.1705763 67.0635019,17.2935745 L69.7930391,17.2935745 C69.808264,16.9281549 69.817399,16.561786 69.8204285,16.1951014 C69.8204285,11.8994238 68.9834644,7.77980649 67.4940278,4.74260207 C66.0045911,1.70539766 63.9845746,-0.000576365893 61.8784,9.31541879e-06 L61.8783956,9.31541879e-06 Z\" id=\"Path\" stroke-width=\"1.00027776\" fill-rule=\"nonzero\" stroke-linejoin=\"round\"></path>\n                                            <polyline id=\"Path\" stroke-width=\"3.449638\" stroke-linecap=\"square\" points=\"0 16.0743902 27 16.0743902 27 16.0743902 27 16.0743902 27 16.0743902\"></polyline>\n                                            <polyline id=\"Path-Copy\" stroke-width=\"3.45\" stroke-linecap=\"square\" points=\"69.3412154 16.0743902 96.4412154 16.0743902 96.4412154 16.0743902 96.4412154 16.0743902 96.4412154 16.0743902\"></polyline>\n                                        </g>\n                                        <g id=\"relay_open{{actuator.id}}\" transform=\"translate(0.000000, 33.170732)\" fill-rule=\"nonzero\">\n                                            <path d=\"M39.250709,0.036993145 C36.5409268,-0.279344691 34.0160538,1.46318254 33.3453935,4.11251418 L0,4.14634146 L0,6.82926829 L33.4385858,6.91350179 C34.1289886,9.22493605 36.2491483,10.8104375 38.6573559,10.8162112 C39.9623614,10.8088356 41.2204787,10.3278685 42.1986641,9.46240051 L58.1531896,20.7317073 L59.7560976,18.4442341 L43.7736143,7.14691742 C43.9695968,6.5730598 44.0672734,5.97012212 44.0625105,5.36362198 C44.017822,2.63078109 41.9604912,0.353330981 39.250709,0.036993145 Z M38.6200789,8.01522358 C37.1583679,8.01522358 35.973417,6.82806111 35.973417,5.36362198 C35.973417,3.89918286 37.1583679,2.71202038 38.6200789,2.71202038 C40.08179,2.71202038 41.2667409,3.89918286 41.2667409,5.36362198 C41.2667409,6.82079964 40.0931444,8.00496177 38.6387174,8.01522358 L38.6200789,8.01522358 Z\" id=\"Shape\"></path>\n                                            <path d=\"M100,4.14634146 L69.6976868,4.12347239 C69.0687317,1.5026095 66.5776027,-0.249323806 63.8756306,0.0290019564 C61.1736586,0.307327719 59.0987544,2.52960071 59.0263399,5.22271055 C58.9539255,7.91582039 60.9064572,10.2446045 63.5896322,10.6653479 C66.2728071,11.0860913 68.8545435,9.46831689 69.6234482,6.88442233 L100,6.82926829 L100,4.14634146 Z M64.4081865,7.97039598 C62.9526565,7.97039598 61.7727162,6.80020295 61.7727162,5.3566967 C61.7727162,3.91319045 62.9526565,2.74299742 64.4081865,2.74299742 C65.8637166,2.74299742 67.0436569,3.91319045 67.0436569,5.3566967 C67.0386624,6.79098414 65.8729092,7.95530668 64.4267462,7.97039598 L64.4081865,7.97039598 Z\" id=\"Shape\"></path>\n                                        </g>\n                                        <g id=\"relay_closed{{actuator.id}}\" transform=\"translate(0.000000, 33.170732)\" fill-rule=\"nonzero\"\n                                           visibility=\"hidden\">\n                                            <path d=\"M39.250709,0.036993145 C36.5409268,-0.279344691 34.0160538,1.46318254 33.3453935,4.11251418 L0,4.14634146 L0,6.82926829 L33.4385858,6.91350179 C34.1289886,9.22493605 36.2491483,10.8104375 38.6573559,10.8162112 C39.9623614,10.8088356 42.9268293,9.75609756 43.6585366,6.82926829 L59.7560976,6.82926829 L59.7560976,4.14634146 L43.6585366,4.14634146 C43.6585366,2.43902439 41.9604912,0.353330981 39.250709,0.036993145 Z M38.6200789,8.01522358 C37.1583679,8.01522358 35.973417,6.82806111 35.973417,5.36362198 C35.973417,3.89918286 37.1583679,2.71202038 38.6200789,2.71202038 C40.08179,2.71202038 41.2667409,3.89918286 41.2667409,5.36362198 C41.2667409,6.82079964 40.0931444,8.00496177 38.6387174,8.01522358 L38.6200789,8.01522358 Z\" id=\"Shape\"></path>\n                                            <path d=\"M100,4.14634146 L69.6976868,4.12347239 C69.0687317,1.5026095 66.5776027,-0.249323806 63.8756306,0.0290019564 C61.1736586,0.307327719 59.0987544,2.52960071 59.0263399,5.22271055 C58.9539255,7.91582039 60.9064572,10.2446045 63.5896322,10.6653479 C66.2728071,11.0860913 68.8545435,9.46831689 69.6234482,6.88442233 L100,6.82926829 L100,4.14634146 Z M64.4081865,7.97039598 C62.9526565,7.97039598 61.7727162,6.80020295 61.7727162,5.3566967 C61.7727162,3.91319045 62.9526565,2.74299742 64.4081865,2.74299742 C65.8637166,2.74299742 67.0436569,3.91319045 67.0436569,5.3566967 C67.0386624,6.79098414 65.8729092,7.95530668 64.4267462,7.97039598 L64.4081865,7.97039598 Z\" id=\"Shape\"></path>\n                                        </g>\n                                    </g>\n                                    <rect id=\"Rectangle\" stroke=\"#000000\" stroke-width=\"3\" x=\"11.5\" y=\"1.5\" width=\"77\" height=\"67\"></rect>\n                                </g>\n                            </g>\n                        </g>\n                    </svg>\n                </div>\n            </div>\n        </div>\n    </div>\n    <div class=\"card-footer\">\n        <div class=\"container\">\n            <div class=\"row\">\n                <div class=\"col\">\n                    <button id=\"actuator_delete_button{{actuator.id}}\" class=\"w-100 btn btn-lg btn-danger\">Delete</button>\n                </div>\n            </div>\n        </div>\n    </div>\n    \n    <script>\n        window.addEventListener(\"DOMContentLoaded\", function () {\n            var delete_button = document.getElementById(\"actuator_delete_button{{actuator.id}}\")\n            var relay_closed = document.getElementById(\"relay_closed{{actuator.id}}\")\n            var relay_open = document.getElementById(\"relay_open{{actuator.id}}\")\n            var relay_title = document.getElementById(\"relayTitleId{{actuator.id}}\")\n\n            delete_button.addEventListener(\"click\", function(){\n                var payload = {\n                    \"port\": \"{{actuator.id}}\"\n                }\n\n                var xhr = new XMLHttpRequest();\n                var url = \"./delete_actuator\";\n                xhr.open(\"POST\", url, false);\n                xhr.setRequestHeader(\"Content-Type\", \"application/json\");\n                var data = JSON.stringify(payload);\n                xhr.send(data);\n                location.reload();\n            });\n\n            var socket = io();\n            socket.on('actuator_change{{actuator.id}}', function(data) {\n                if (data.value){\n                    relay_open.style.visibility = \"hidden\"\n                    relay_closed.style.visibility = \"visible\"\n                    relay_title.innerHTML = \"Relay on\"\n                } else {\n                    relay_open.style.visibility = \"visible\"\n                    relay_closed.style.visibility = \"hidden\"\n                    relay_title.innerHTML = \"Relay off\"\n                }\n\n                console.log('actuator change received');\n                console.log(data);\n            });\n        })\n    </script>\n</div>"
  },
  {
    "path": "counterfit-app/src/CounterFit/templates/serial_sensor_create_settings.html",
    "content": "<div class=\"row\" style=\"margin-top: 20px;\">\n    <div class=\"col\">\n        <label for=\"new_sensor_port\">Port:</label>\n    </div>\n    <div class=\"col\">\n        <input class=\"form-control\" type=\"text\" id=\"new_sensor_port\" value=\"/dev/ttyAMA0\" style=\"width: 100%;\"/>\n    </div>\n</div>"
  },
  {
    "path": "counterfit-app/src/__main__.py",
    "content": "from CounterFit.counterfit import main\n\nmain()\n"
  },
  {
    "path": "counterfit-app/src/tests/__init__.py",
    "content": ""
  },
  {
    "path": "counterfit-app/src/tests/route.gpx",
    "content": "<?xml version=\"1.0\"?>\n<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" version=\"1.1\" creator=\"AllTrails.com\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n  <metadata>\n    <name><![CDATA[Trail Planner Map]]></name>\n    <desc><![CDATA[]]></desc>\n    <link href=\"http://www.alltrails.com\">\n      <text>AllTrails, LLC</text>\n    </link>\n    <bounds minlat=\"47.72935\" minlon=\"-122.26477\" maxlat=\"47.73552\" maxlon=\"-122.25581\"/>\n  </metadata>\n  <trk>\n    <name><![CDATA[Trail Planner Map]]></name>\n    <desc><![CDATA[]]></desc>\n    <src>AllTrails</src>\n    <trkseg>\n      <trkpt lat=\"47.73481\" lon=\"-122.257\">\n        <ele>107.05</ele>\n      </trkpt>\n      <trkpt lat=\"47.73481\" lon=\"-122.25701\">\n        <ele>107.05</ele>\n      </trkpt>\n      <trkpt lat=\"47.73476\" lon=\"-122.25682\">\n        <ele>108.0</ele>\n      </trkpt>\n    </trkseg>\n  </trk>\n</gpx>\n"
  },
  {
    "path": "counterfit-app/src/tests/test_serial_sensors.py",
    "content": "# pylint: disable=protected-access,line-too-long\nimport datetime\nimport time\n\nfrom CounterFit.serial_sensors import GPSSensor, GPSValueType\n\n\ndef test_decimal_degree_conversion():\n    assert GPSSensor._decimal_decrees_to_ddmmm(47.653814) == \"4739.22884\"\n    assert GPSSensor._decimal_decrees_to_ddmmm(-47.09565) == \"4705.739\"\n    assert GPSSensor._decimal_decrees_to_ddmmm(-47) == \"4700.0\"\n\n\ndef test_checksum():\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005206.768,5227.561,N,01230.806,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"68\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"GPGGA,005207.768,5249.958,N,01308.049,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"6E\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005208.768,5310.190,N,01409.023,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"6A\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005209.768,5300.094,N,01450.881,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"63\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005210.768,5148.313,N,01608.005,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"62\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"# $GPGGA,005211.768,5142.192,N,01454.836,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"61\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005212.768,5135.443,N,01332.439,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"6F\"\n    )\n    assert (\n        GPSSensor._build_checksum(\n            \"$GPGGA,005213.768,5153.810,N,01259.150,E,1,12,1.0,0.0,M,0.0,M,,*00\"\n        )\n        == \"62\"\n    )\n\n\ndef test_gps_empty_read_line():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    line = gps.read_line()\n    assert line == \"\"\n\n\ndef test_gps_empty_read():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    char = gps.read()\n    assert char == \"\"\n\n\ndef add_checksum_to_expected(expected: str) -> str:\n    checksum = GPSSensor._build_checksum(expected)\n    expected += checksum\n    return expected\n\n\ndef test_gps_time_setting_when_using_lat_lon_n_e():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.LATLON\n\n    gps.lat = 47\n    gps.lon = 122\n    gps.number_of_satellites = 3\n\n    current_utc = datetime.datetime.utcnow()\n\n    line = gps.read_line()\n\n    expected = add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4700.0,N,12200.0,E,1,3,,0,M,0,M,,*\"\n    )\n\n    assert line == expected\n    assert gps.read_line() == \"\"\n\n\ndef test_gps_time_setting_when_using_lat_lon_s_w():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.LATLON\n\n    gps.lat = -47\n    gps.lon = -122\n    gps.number_of_satellites = 3\n\n    current_utc = datetime.datetime.utcnow()\n\n    expected = add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4700.0,S,12200.0,W,1,3,,0,M,0,M,,*\"\n    )\n\n    assert gps.read_line() == expected\n    assert gps.read_line() == \"\"\n\n\ndef test_gps_nmea():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    assert (\n        gps.read_line()\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n    assert gps.read_line() == \"\"\n\n\ndef test_gps_nmea_repeat():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n    gps.repeat = True\n\n    for _ in range(0, 20):\n        line = gps.read_line()\n        if line == \"\":\n            time.sleep(1)\n            line = gps.read_line()\n\n        assert (\n            line\n            == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n        )\n\n\ndef test_gps_nmea_repeat_has_delay():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n    gps.repeat = True\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    time.sleep(1)\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n\ndef test_gps_nmea_multiple_sentences_has_delay():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n        + \"\\n\"\n        + \"$GNGGA,020604.001,4739.228833333,S,12207.031866667,E,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    time.sleep(1.5)\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,S,12207.031866667,E,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n\ndef test_gps_nmea_multiple_sentences_has_delay_only_for_gga():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n        + \"\\n\"\n        + \"$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30\"\n        + \"\\n\"\n        + \"$GNGGA,020604.001,4739.228833333,S,12207.031866667,E,1,3,,164.7,M,-17.1,M,,*67\"\n        + \"\\n\"\n        + \"$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30\"\n    )\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    line = gps.read_line()\n    assert line == \"$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    time.sleep(1)\n\n    line = gps.read_line()\n    assert (\n        line\n        == \"$GNGGA,020604.001,4739.228833333,S,12207.031866667,E,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    line = gps.read_line()\n    assert line == \"$GPGSA,A,3,01,02,03,04,05,06,07,08,09,10,11,12,1.0,1.0,1.0*30\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n    line = gps.read_line()\n    assert line == \"\"\n\n\ndef test_gps_gpx():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.GPX\n\n    with open(\"./src/tests/route.gpx\", \"r\", encoding=\"UTF-8\") as gpx_file:\n        gps.gpx_file_contents = gpx_file.read()\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.42,W,1,3,,0,M,0,M,,*\"\n    )\n\n    time.sleep(2)\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.4206,W,1,3,,0,M,0,M,,*\"\n    )\n\n    time.sleep(2)\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0856,N,12215.4092,W,1,3,,0,M,0,M,,*\"\n    )\n\n    assert gps.read_line() == \"\"\n\n\ndef test_gps_gpx_repeat():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.GPX\n\n    with open(\"./src/tests/route.gpx\", \"r\", encoding=\"UTF-8\") as gpx_file:\n        gps.gpx_file_contents = gpx_file.read()\n\n    gps.repeat = True\n\n    for _ in range(0, 20):\n        line = gps.read_line()\n        if line == \"\":\n            time.sleep(1)\n            line = gps.read_line()\n\n        current_utc = datetime.datetime.utcnow()\n        assert line == add_checksum_to_expected(\n            f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.42,W,1,3,,0,M,0,M,,*\"\n        )\n\n        line = gps.read_line()\n        if line == \"\":\n            time.sleep(1)\n            line = gps.read_line()\n\n        current_utc = datetime.datetime.utcnow()\n        assert line == add_checksum_to_expected(\n            f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.4206,W,1,3,,0,M,0,M,,*\"\n        )\n\n        line = gps.read_line()\n        if line == \"\":\n            time.sleep(1)\n            line = gps.read_line()\n\n        current_utc = datetime.datetime.utcnow()\n        assert line == add_checksum_to_expected(\n            f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0856,N,12215.4092,W,1,3,,0,M,0,M,,*\"\n        )\n\n\ndef test_setting_lat_lon_clears_value():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    gps.value_type = GPSValueType.LATLON\n\n    gps.lat = -47\n    gps.lon = -122\n    gps.number_of_satellites = 3\n\n    current_utc = datetime.datetime.utcnow()\n\n    expected = add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4700.0,S,12200.0,W,1,3,,0,M,0,M,,*\"\n    )\n\n    assert gps.read_line() == expected\n    assert gps.read_line() == \"\"\n\n\ndef test_setting_gpx_clears_value():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    gps.value_type = GPSValueType.GPX\n\n    with open(\"./src/tests/route.gpx\", \"r\", encoding=\"UTF-8\") as gpx_file:\n        gps.gpx_file_contents = gpx_file.read()\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.42,W,1,3,,0,M,0,M,,*\"\n    )\n\n    time.sleep(2)\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0886,N,12215.4206,W,1,3,,0,M,0,M,,*\"\n    )\n\n    time.sleep(2)\n\n    current_utc = datetime.datetime.utcnow()\n    assert gps.read_line() == add_checksum_to_expected(\n        f\"$GPGGA,{current_utc.hour:02d}{current_utc.minute:02d}{current_utc.second:02}.00,4744.0856,N,12215.4092,W,1,3,,0,M,0,M,,*\"\n    )\n\n    assert gps.read_line() == \"\"\n\n\ndef test_setting_nmea_clears_value():\n    gps = GPSSensor(\"/dev/ttyAMA0\")\n\n    gps.value_type = GPSValueType.GPX\n\n    with open(\"./src/tests/route.gpx\", \"r\", encoding=\"UTF-8\") as gpx_file:\n        gps.gpx_file_contents = gpx_file.read()\n\n    gps.value_type = GPSValueType.NMEA\n\n    gps.raw_nmea = (\n        \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n\n    assert (\n        gps.read_line()\n        == \"$GNGGA,020604.001,4739.228833333,N,12207.031866667,W,1,3,,164.7,M,-17.1,M,,*67\"\n    )\n    assert gps.read_line() == \"\"\n"
  },
  {
    "path": "counterfit-app/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "requirements.txt",
    "content": "twine=4.0.2\nblack=23.1.0"
  },
  {
    "path": "samples/grove/button-temperature-sensor-controlled-relay/README.md",
    "content": "# Relay controlled by temperature sensor and button sample\n\nThis sample shows how to use the CounterFit Grove shims to simulate a relay controlled by a temperature sensor and button setup.\nThe temperature value is read whenever the button is pressed, and the relay is activated if the temperature is under 38 degrees celsius.\n\n## Setup\n\n* Create a Python virtual environment:\n\n    ```sh\n    python3 -m venv .venv\n    ```\n\n* Activate the environment:\n\n    macOS/Linux:\n\n    ```sh\n    source ./.venv/bin/activate\n    ```\n\n    Windows:\n\n    ```cmd\n    .venv\\Scripts\\activate.bat\n    ```\n\n* Install the required pip packages:\n\n    ```sh\n    pip install -r requirements.txt\n    ```\n\nThis will install the [CounterFit](https://github.com/CounterFit-IoT/CounterFit/tree/main/counterfit-app) app and the [CounterFit Grove Shims](https://github.com/CounterFit-IoT/CounterFit/tree/main/shims).\n\n## Configure the hardware\n\n* Launch the CounterFit app:\n\n    ```sh\n    CounterFit\n    ```\n\n    This will launch the app in a web browser, running on port 5000.\n\n    > If you want to change the port, pass it is as the `--port` parameter when launching `CounterFit`\n\n* Create a **button** on **pin 1**, and a **temperature sensor** on **pin 2**, and a **relay** on **pin 3**\n![Screenshot of the used setup](https://github.com/AChillFeeder/CounterFit/blob/temperature_button_relay_example/samples/grove/button-temperature-sensor-controlled-relay/assets/HardwareSetup.png)\n\n## Run the code\n\n* Run the `app.py` file in the same virtual environment (you will need a new terminal or CMD)\n\n    > If you used a different port, then you will need to edit the port use to initialize the CounterFit connection:\n    >\n    > ```python\n    > CounterFitConnection.init('127.0.0.1', <port>)\n    > ```\n    >\n    > Replace `<port>` with the port you used.\n\n* Adjust the values in CounterFit for the temperature level. When the value is less than 38 and the button is pressed, the relay will activate."
  },
  {
    "path": "samples/grove/button-temperature-sensor-controlled-relay/main.py",
    "content": "\n\n# In this example:\n# We will create a circuit where, when a button is pressed, we will check the current temperature\n# If the recorded temperature is under 38 celsius, the relay will be activated\n\n\n# PIN 1: Button\n# PIN 2: Temperature sensor\n# PIN 3: Relay\n\nimport time\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_grove.grove_relay import GroveRelay\n\nCounterFitConnection.init('127.0.0.1', 5000)\n\nrelay = GroveRelay(3)\n\nwhile True:\n    \n    button = CounterFitConnection.get_sensor_boolean_value(1) # The button returns boolean values, True for pressed and False for released\n\n    if button:\n        print(\"The button has been pressed\")\n        temperature = CounterFitConnection.get_sensor_float_value(2) # Get temperature value\n\n        print(f\"Recorded temperature is {temperature}\")\n\n        if temperature < 38:\n            print(\"Activate relay\")\n            relay.on()\n            time.sleep(5)\n            relay.off()\n\n    time.sleep(0.5) # Used a short sleep timer as not to miss a button press when the script is on break\n"
  },
  {
    "path": "samples/grove/button-temperature-sensor-controlled-relay/requirements.txt",
    "content": "counterfit\ncounterfit_shims_grove"
  },
  {
    "path": "samples/grove/light-sensor-controlled-led/README.md",
    "content": "# Light sensor controlled LED sample\n\nThis sample shows how to use the CounterFit Grove shims to simulate an LED and a light sensor, controlling the LED based on the light levels read from the sensor.\n\n## Setup\n\n* Create a Python virtual environment:\n\n    ```sh\n    python3 -m venv .venv\n    ```\n\n* Activate the environment:\n\n    macOS/Linux:\n\n    ```sh\n    source ./.venv/bin/activate\n    ```\n\n    Windows:\n\n    ```cmd\n    .venv\\Scripts\\activate.bat\n    ```\n\n* Install the required pip packages:\n\n    ```sh\n    pip install -r requirements.txt\n    ```\n\nThis will install the [CounterFit](https://github.com/CounterFit-IoT/CounterFit/tree/main/counterfit-app) app and the [CounterFit Grove Shims](https://github.com/CounterFit-IoT/CounterFit/tree/main/shims/grove).\n\n## Configure the hardware\n\n* Launch the CounterFit app:\n\n    ```sh\n    CounterFit\n    ```\n\n    This will launch the app in a web browser, running on port 5000.\n\n    > If you want to change the port, pass it is as the `--port` parameter when launching `CounterFit`\n\n* Create a light sensor on pin 1, and an LED actuator on pin 2\n\n## Run the code\n\n* Run the `app.py` file in the same virtual environment (you will need a new terminal or CMD)\n\n    > If you used a different port, then you will need to edit the port use to initialize the CounterFit connection:\n    >\n    > ```python\n    > CounterFitConnection.init('127.0.0.1', <port>)\n    > ```\n    >\n    > Replace `<port>` with the port you used.\n\n* Adjust the values in CounterFit for the light level. When the value is less than 200, the LED will light up.\n"
  },
  {
    "path": "samples/grove/light-sensor-controlled-led/app.py",
    "content": "import time\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor\nfrom counterfit_shims_grove.grove_led import GroveLed\n\nCounterFitConnection.init('127.0.0.1', 5000)\n\nlight_sensor = GroveLightSensor(1)\nled = GroveLed(2)\n\nwhile True:\n    sensor_value = light_sensor.light\n    print(f'Light sensor reading: {sensor_value}')\n\n    if sensor_value > 200:\n        print('Turning LED off')\n        led.off()\n    else:\n        print('Turning LED on')\n        led.on()\n\n    time.sleep(2)"
  },
  {
    "path": "samples/grove/light-sensor-controlled-led/requirements.txt",
    "content": "counterfit\ncounterfit_shims_grove"
  },
  {
    "path": "shims/CounterFitConnection/.gitignore",
    "content": "test_image.png"
  },
  {
    "path": "shims/CounterFitConnection/.pylintrc",
    "content": "[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=140\n\n[MESSAGES CONTROL]\ndisable=trailing-whitespace"
  },
  {
    "path": "shims/CounterFitConnection/README.md",
    "content": "# CounterFit Shims - CounterFit connection\n\n![CounterFit Connection](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-connection)](https://pypi.org/project/counterfit-connection)\n\nA core connection library to use with the [CounterFit virtual IoT device app](https://github.com/CounterFit-IoT/CounterFit).\n\nUse this when writing a shim library.\n"
  },
  {
    "path": "shims/CounterFitConnection/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/CounterFitConnection/counterfit_connection.py",
    "content": "'''\nProvides a connection to the CounterFit Virtual IoT Device app. This connection is re-used by all the virtual sensors.\n\nExamples:\n\n    When connecting to localhost on the default port:\n\n    .. code-block:: python\n\n        from counterfit_shims_grove.counterfit_connection import CounterFitConnection\n\n        CounterFitConnection.init()\n\n\n    When connection to another computer on a different port:\n\n    .. code-block:: python\n\n        from counterfit_shims_grove.counterfit_connection import CounterFitConnection\n\n        CounterFitConnection.init('192.168.197.1', 5050)\n'''\n# pylint: disable=duplicate-code,bare-except\n\nfrom base64 import b64decode\nimport io\nimport requests\n\nclass CounterFitConnection:\n    '''\n    Connects to the CounterFit Virtual IoT device on a give host and port, and allows the value of sensors to be read,\n    as well as setting the value of actuators.\n    '''\n    base_url = ''\n\n    @staticmethod\n    def init(hostname: str = 'localhost', port: int = 5000) -> None:\n        '''\n        Initializes the connection to the Virtual IoT Device running on the given url and port\n        '''\n        CounterFitConnection.base_url = f'http://{hostname}:{str(port)}/'\n        requests.post(CounterFitConnection.base_url + 'connect')\n    \n    @staticmethod\n    def get_sensor_float_value(port: int) -> float:\n        '''\n        Reads a float value from the sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'sensor_value?port=' + str(port))\n        return float(response.json()['value'])\n    \n    @staticmethod\n    def get_sensor_int_value(port: int) -> int:\n        '''\n        Reads an integer value from the sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'sensor_value?port=' + str(port))\n        return int(response.json()['value'])\n    \n    @staticmethod\n    def get_sensor_boolean_value(port: int) -> bool:\n        '''\n        Reads a bool value from the sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'sensor_value?port=' + str(port))\n        return bool(response.json()['value'])\n    \n    @staticmethod\n    def read_serial_sensor_char(port: str) -> str:\n        '''\n        Reads a character from the serial sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'serial_sensor_character?port=' + port)\n        return str(response.json()['value'])\n    \n    @staticmethod\n    def read_serial_sensor_line(port: str) -> str:\n        '''\n        Reads a line from the serial sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'serial_sensor_line?port=' + port)\n        return str(response.json()['value'])\n    \n    @staticmethod\n    def read_binary_sensor(port: str) -> io.BytesIO:\n        '''\n        Reads a character from the serial sensor on the given port\n        '''\n        response = requests.get(CounterFitConnection.base_url + 'binary_sensor_data?port=' + port)\n        msg = b64decode(response.json()['value'])\n        return io.BytesIO(msg)\n    \n    @staticmethod\n    def set_actuator_float_value(port: int, value: float) -> None:\n        '''\n        Sends a float value to the actuator on the given port\n        '''\n        requests.post(CounterFitConnection.base_url + 'actuator_value?port=' + str(port), json= {'value':value})\n    \n    @staticmethod\n    def set_actuator_boolean_value(port: int, value: bool) -> None:\n        '''\n        Sends a bool value to the actuator on the given port\n        '''\n        requests.post(CounterFitConnection.base_url + 'actuator_value?port=' + str(port), json= {'value':value})\n    \n    @staticmethod\n    def is_connected() -> bool:\n        '''\n        Determines if CounterFit is running\n        '''\n        try:\n            requests.post(CounterFitConnection.base_url + 'connect')\n            return True\n        except:\n            return False\n"
  },
  {
    "path": "shims/CounterFitConnection/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\n"
  },
  {
    "path": "shims/CounterFitConnection/setup.py",
    "content": "'''\nConnection library for use with the CounterFit Virtual IoT Device app\n'''\n\n# pylint: disable=redefined-builtin\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-connection',\n    py_modules=['counterfit_connection'],\n    version='0.1.0.dev5',\n    description='Connection library for use with the CounterFit Virtual IoT Device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot grove seeed virtual hardware\",\n    install_requires=['requests'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/CounterFitConnection/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/CounterFitConnection/tests/test_counterfit_connection.py",
    "content": "'''\nTests the CounterFit app connection\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument,duplicate-code\n\nimport pytest\nimport time\n\nfrom counterfit_connection import CounterFitConnection\n\ndef test_init_counterfit_device():\n    '''\n    Tests the connection. You should see the CounterFit app status change to connected\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\n# def test_get_sensor_bool_value():\n#     '''\n#     Tests reading a True boolean value from a boolean sensor on port 0\n#     '''\n#     CounterFitConnection.init('127.0.0.1', 5000)\n#     assert CounterFitConnection.get_sensor_boolean_value(0)\n\n# def test_read_serial_char():\n#     '''\n#     Tests reading a character from a serial sensor on port /dev/ttyAMA0 containing the text 'hello'\n#     '''\n#     CounterFitConnection.init('127.0.0.1', 5000)\n#     assert CounterFitConnection.read_serial_sensor_char('/dev/ttyAMA0') == 'h'\n#     assert CounterFitConnection.read_serial_sensor_char('/dev/ttyAMA0') == 'e'\n#     assert CounterFitConnection.read_serial_sensor_char('/dev/ttyAMA0') == 'l'\n#     assert CounterFitConnection.read_serial_sensor_char('/dev/ttyAMA0') == 'l'\n#     assert CounterFitConnection.read_serial_sensor_char('/dev/ttyAMA0') == 'o'\n\n# def test_read_serial_line():\n#     '''\n#     Tests reading a line from a serial sensor on port /dev/ttyAMA0 containing the text 'hello\\nworld'\n#     '''\n#     CounterFitConnection.init('127.0.0.1', 5000)\n#     assert CounterFitConnection.read_serial_sensor_line('/dev/ttyAMA0') == 'hello'\n#     assert CounterFitConnection.read_serial_sensor_line('/dev/ttyAMA0') == 'world'\n\ndef test_camera_image():\n    '''\n    Tests reading an image from a camera sensor. The image is saved locally\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n    image_data = CounterFitConnection.read_binary_sensor('Picamera')\n\n    with open('test_image.png', 'wb') as image_file:\n        image_file.write(image_data.read())\n\ndef test_is_connected():\n    '''\n    Tests is connected. Make sure counterfit is running\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n    assert CounterFitConnection.is_connected()\n\ndef test_is_connected_is_false():\n    '''\n    Tests is connected. Make sure counterfit is running until you see a message telling you to close it\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n    print(\"Please close counterfit\")\n    time.sleep(10)\n    assert not CounterFitConnection.is_connected()\n"
  },
  {
    "path": "shims/CounterFitConnection/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/.pylintrc",
    "content": "[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=140\n\n[MESSAGES CONTROL]\ndisable=trailing-whitespace"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/README.md",
    "content": "# CounterFit Shims - Grove\n\n![Grove Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-seeed-python-dht)](https://pypi.org/project/counterfit-shims-seeed-python-dht)\n\nShims for the Seeed Grove DHT digital temperature and humidity sensor to use with the [CounterFit virtual IoT device app](https://github.com/CounterFit-IoT/CounterFit).\n\nSee the [Seeed Python DHT Docs](https://github.com/Seeed-Studio/Seeed_Python_DHT) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate hardware created.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-seeed-python-dht\n    ```\n\n* Import the Grove modules as normal, but using the `counterfit_shims_seeed_python_dht` package instead of the `seeed-python-dht` package, as well as importing the `CounterFitConnection` from the `counterfit_shims_grove.counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    from counterfit_shims_seeed_python_dht import DHT\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your Grove code as usual, setting the pins to match the ones you set in the CounterFit app. To keep to the interface that the `DHT` specifies, only one pin can be passed to the constructor. The sensor assumes the pin given is for the humidity sensor, and the temperature is on the next pin.\n\n    For example, create a humidity sensor on pin 1, and a temperature sensor on pin 2. Then pass 1 to the `DHT` constructor.\n\n    ```python\n    sensor =.DHT(\"11\", 1)\n    humi, temp = sensor.read()\n    ```\n"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/counterfit_shims_seeed_python_dht.py",
    "content": "'''\nThis is the code for\n    - `Grove - DHT11 digital temperature and humidity sensor <https://www.seeedstudio.com/Grove-Temperature-Humidity-Sensor-DHT11.html>`_\n\nExamples:\n\n    .. code-block:: python\n        import time\n        from counterfit_shims_seeed_python_dht.counterfit_connection import CounterFitConnection\n        import counterfit_shims_seeed_python_dht.seeed_dht\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        # connect to a humidity sensor on pin 12 and a temperature sensor on pin 13\n        PIN   = 12\n        sensor = seeed_dht.DHT(\"11\", 12)\n\n        humi, temp = sensor.read()\n'''\n# pylint: disable=too-few-public-methods,unused-argument\n\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['DHT']\n\nclass DHT():\n    '''\n    Class for Grove DHT\n    Args:\n        pin(int): The pin for the humidity sensor. The temperature sensor needs to be on the next pin.\n    '''\n    def __init__(self, dht_type, pin = 12,bus_num = 1):\n        self.__humidity_pin = pin\n        self.__temperature_pin = pin + 1\n\n    def read(self, retries = 15):\n        '''\n        Read the humidity and temperature from the sensor\n        '''\n        humidity = CounterFitConnection.get_sensor_float_value(self.__humidity_pin)\n        temperature = CounterFitConnection.get_sensor_float_value(self.__temperature_pin)\n\n        return humidity, temperature\n"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/setup.py",
    "content": "'''\nShims for the Seeed Python DHT library for use with the CounterFit Virtual IoT Device app\n'''\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-seeed-python-dht',\n    py_modules=['counterfit_shims_seeed_python_dht'],\n    version='0.1.0.dev1',\n    description='Shims for the Seeed Grove DHT for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot grove seeed virtual hardware\",\n    install_requires=['requests','counterfit-connection'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/tests/test_counterfit_shims_seeed_python_dht.py",
    "content": "'''\nTests the Grove DHT.\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running, with a humidity sensor\non pin 0 and a temperature sensor on pin 1.\n\nThe humidity sensor should be set to 50%, and the temperature sensor to 25°C\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_seeed_python_dht import DHT\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_humidity_and_temperature(init_counterfit_device):\n    '''\n    Tests values returned from the DHT sensor\n    '''\n    sensor = DHT(\"11\", 12)\n\n    humi, temp = sensor.read()\n\n\n    assert humi == 50.0\n    assert temp == 25.0\n"
  },
  {
    "path": "shims/SeeedStudios/Seeed_Python_DHT/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/SeeedStudios/grove_py/.gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n\n.vscode"
  },
  {
    "path": "shims/SeeedStudios/grove_py/.pylintrc",
    "content": "[FORMAT]\n\n# Maximum number of characters on a single line.\nmax-line-length=140\n\n[MESSAGES CONTROL]\ndisable=trailing-whitespace\n\n[SIMILARITIES]\ndisable=duplicate-code"
  },
  {
    "path": "shims/SeeedStudios/grove_py/README.md",
    "content": "# CounterFit Shims - Grove\n\n![Grove Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-grove)](https://pypi.org/project/counterfit-shims-grove)\n\nShims for the Seeed Grove sensors and actuators to use with the [CounterFit virtual IoT device app](https://github.com/CounterFit-IoT/CounterFit).\n\nSee the [Grove Py Docs](https://github.com/Seeed-Studio/grove.py) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate hardware created.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-grove\n    ```\n\n* Import the Grove modules as normal, but using the `counterfit_shims_grove` package instead of the `grove` package, as well as importing the `CounterFitConnection` from the `counterfit_shims_grove.counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor\n    from counterfit_shims_grove.grove_led import GroveLed\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your Grove code as usual, setting the pins to match the ones you set in the CounterFit app:\n\n    ```python\n    light_sensor = GroveLightSensor(2)\n    sensor_value = light_sensor.light\n    ```\n\n## Implemented shims\n\nNot all the Grove sensor and actuators are currently implemented:\n\n| Sensor/Actuator |\n| ------ |\n| grove_led |\n| grove_light_sensor_v1_2 |\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/SeeedStudios/grove_py/counterfit_shims_grove/__init__.py",
    "content": "'''\nCounterFit Virtual IoT Device shims for Grove sensors.\n\nThis library provides shims that mimic the Grove Python libraries from:\n    https://github.com/Seeed-Studio/grove.py\n\nThese shims don't communicate with real hardware, instead they communicate with a running instance of\nthe CounterFit Virtual IoT Device app\n'''\n\nfrom .grove_light_sensor_v1_2 import GroveLightSensor\nfrom .grove_led import GroveLed\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/counterfit_shims_grove/adc.py",
    "content": "'''\nThis is the code for\n    - `Grove Base Hat for RPi      <https://www.seeedstudio.com/Grove-WS2813-RGB-LED-Strip-Waterproof-60-LED-m-1m-p-3126.html>`_\n    - `Grove Base Hat for RPi Zero <https://www.seeedstudio.com/Grove-Base-Hat-for-Raspberry-Pi-Zero-p-3187.html>`_\n\nGrove Base Hat incorporates a micro controller STM32F030F4.\n\nRaspberry Pi does not have ADC unit, so we use an external chip\nto transmit analog data to raspberry pi.\n\nExamples:\n    .. code-block:: python\n\n        import time\n        from counterfit_connection import CounterFitConnection\n        from counterfit_shims_grove.adc import ADC\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        adc = ADC()\n        while True:\n            # Read channel 0(Slot A0)\n            print(adc.read(0))\n            time.sleep(1)\n\n'''\n# pylint: disable=no-self-use\n\n# pylint: disable=import-error\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = [\"ADC\"]\n\nclass ADC():\n    '''\n    Class ADC for the ADC unit on Grove Base Hat for RPi.\n\n    Args:\n        address(int): optional, i2c address of the ADC unit, default 0x04\n    '''\n    def __init__(self, address = 0x04):\n        pass\n\n    def read_raw(self, channel):\n        '''\n        Read the raw data of ADC unit, with 12 bits resolution.\n\n        Args:\n            channel (int): 0 - 7, specify the channel to read\n\n        Returns:\n            (int): the adc result, in [0 - 4095]\n        '''\n        raise NotImplementedError()\n\n    # read input voltage (mV)\n    def read_voltage(self, channel):\n        '''\n        Read the voltage data of ADC unit.\n\n        Args:\n            channel (int): 0 - 7, specify the channel to read\n\n        Returns:\n            (int): the voltage result, in mV\n        '''\n        raise NotImplementedError()\n\n    # input voltage / output voltage (%)\n    def read(self, channel):\n        '''\n        Read the ratio between channel input voltage and power voltage (most time it's 3.3V).\n\n        Args:\n            channel (int): 0 - 7, specify the channel to read\n\n        Returns:\n            (int): the ratio, in 0.1%\n        '''\n        return CounterFitConnection.get_sensor_int_value(channel)\n\n    @property\n    def name(self):\n        '''\n        Get the Hat name.\n\n        Returns:\n            (string): could be :class:`RPI_HAT_NAME` or :class:`RPI_ZERO_HAT_NAME`\n        '''\n        return 'Virtual Grove Hat'\n\n    @property\n    def version(self):\n        '''\n        Get the Hat firmware version.\n\n        Returns:\n            (int): firmware version\n        '''\n        return 1\n\n    # pylint: disable=invalid-name\n    def read_register(self, n):\n        '''\n        Read the ADC Core (through I2C) registers\n\n        Grove Base Hat for RPI I2C Registers\n\n            - 0x00 ~ 0x01: \n            - 0x10 ~ 0x17: ADC raw data\n            - 0x20 ~ 0x27: input voltage\n            - 0x29: output voltage (Grove power supply voltage)\n            - 0x30 ~ 0x37: input voltage / output voltage\n\n        Args:\n            n(int): register address.\n\n        Returns:\n            (int) : 16-bit register value.\n        '''\n        raise NotImplementedError()\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/counterfit_shims_grove/grove_led.py",
    "content": "'''\nThis is the code for\n    - `Grove - Red LED    <https://www.seeedstudio.com/Grove-Red-LED-p-1142.html>`_\n    - `Grove - Green LED  <https://www.seeedstudio.com/Grove-Green-LED-p-1144.html>`_\n    - `Grove - Purple LED <https://www.seeedstudio.com/Grove-Purple-LED-3m-p-1143.html>`_\n    - `Grove - White LED  <https://www.seeedstudio.com/Grove-White-LED-p-1140.html>`_\n\nExamples:\n\n    .. code-block:: python\n        import time\n        from counterfit_connection import CounterFitConnection\n        from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        # connect to pin 5(slot D5)\n        PIN   = 5\n        led = GroveLed(PIN)\n        while True:\n            led.on()\n            time.sleep(1)\n            led.off()\n            time.sleep(1)\n'''\n\n# pylint: disable=import-error\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['GroveLed']\n\nclass GroveLed():\n    '''\n    Class for Grove - XXXX Led\n    Args:\n        pin(int): number of digital pin the led connected.\n    '''\n    def __init__(self, pin):\n        self.__pin = pin\n\n    # pylint: disable=invalid-name\n    def on(self) -> None:\n        '''\n        light on the led\n        '''\n        CounterFitConnection.set_actuator_boolean_value(self.__pin, True)\n\n    def off(self) -> None:\n        '''\n        light off the led\n        '''\n        CounterFitConnection.set_actuator_boolean_value(self.__pin, False)\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/counterfit_shims_grove/grove_light_sensor_v1_2.py",
    "content": "'''\nThis is the shim code for\n    - `Grove - Light Sensor <https://www.seeedstudio.com/Grove-Light-Sensor-v1.2-p-2727.html>`_\n\nExamples:\n\n    .. code-block:: python\n\n        import time\n        from counterfit_connection import CounterFitConnection\n        from counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        # connect to alalog pin 2(slot A2)\n        PIN = 2\n\n        sensor = GroveLightSensor(pin)\n\n        print('Detecting light...')\n        while True:\n            print('Light value: {0}'.format(sensor.light))\n            time.sleep(1)\n'''\n# pylint: disable=too-few-public-methods\n\n# pylint: disable=import-error\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['GroveLightSensor']\n\nclass GroveLightSensor:\n    '''\n    Grove Light Sensor class\n\n    Args:\n        pin(int): number of analog pin/channel the sensor connected.\n    '''\n    def __init__(self, pin: int):\n        self.__pin = pin\n\n    @property\n    def light(self) -> int:\n        '''\n        Get the light strength value, maximum value is 1023\n        '''\n        return CounterFitConnection.get_sensor_int_value(self.__pin)\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/counterfit_shims_grove/grove_relay.py",
    "content": "'''\nThis is the code for\n    - `Grove - Relay <https://www.seeedstudio.com/s/Grove-Relay-p-769.html>`_\n\nExamples:\n\n    .. code-block:: python\n\n        import time\n        from counterfit_connection import CounterFitConnection\n        from counterfit_shims_grove.grove_relay import GroveRelay\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        # connect to pin 5(slot D5)\n        PIN   = 5\n        relay = GroveRelay(PIN)\n\n        while True:\n            relay.on()\n            time.sleep(1)\n            relay.off()\n            time.sleep(1)\n'''\n\n# pylint: disable=import-error\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = [\"GroveRelay\"]\n\nclass GroveRelay():\n    '''\n    Class for Grove - Relay\n\n    Args:\n        pin(int): number of digital pin the relay connected.\n    '''\n    def __init__(self, pin):\n        self.__pin = pin\n\n    # pylint: disable=invalid-name\n    def on(self) -> None:\n        '''\n        light on the led\n        '''\n        CounterFitConnection.set_actuator_boolean_value(self.__pin, True)\n\n    def off(self) -> None:\n        '''\n        light off the led\n        '''\n        CounterFitConnection.set_actuator_boolean_value(self.__pin, False)\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/setup.py",
    "content": "'''\nShims for the Seeed Grove Py library for use with the CounterFit Virtual IoT Device app\n'''\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import find_packages, setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-grove',\n    packages=find_packages(include=['counterfit_shims_grove']),\n    version='0.1.4.dev5',\n    description='Shims for the Seeed Grove sensors for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot grove seeed virtual hardware\",\n    install_requires=['requests','counterfit-connection'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/SeeedStudios/grove_py/tests/test_grove_led.py",
    "content": "'''\nTests the Grove LED shim.\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running, with an LED actuator\non pin 1.\n\nUncomment the relevant test below and run it to see the LED change state\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_grove.grove_led import GroveLed\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_turn_led_on(init_counterfit_device):\n    '''\n    Tests the on method of the Grove LED shim\n    '''\n    sensor = GroveLed(1)\n    sensor.on()\n\ndef test_turn_led_off(init_counterfit_device):\n    '''\n    Tests the off method of the Grove LED shim\n    '''\n    sensor = GroveLed(1)\n    sensor.off()\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/tests/test_grove_light_sensor_v1_2.py",
    "content": "'''\nTests the Grove light sensor shim.\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running, with a light sensor\non pin 1 set to a value of 50\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_grove.grove_light_sensor_v1_2 import GroveLightSensor\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_light_sensor_light_is_50(init_counterfit_device):\n    '''\n    Tests the light property of the Grove Light Sensor shim\n    '''\n    sensor = GroveLightSensor(0)\n    assert sensor.light == 50\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/tests/test_grove_relay.py",
    "content": "'''\nTests the Grove Relay shim.\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running, with a relay actuator\non pin 1.\n\nUncomment the relevant test below and run it to see the relay change state\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_grove.grove_relay import GroveRelay\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\n# def test_turn_relay_on(init_counterfit_device):\n#     '''\n#     Tests the on method of the Grove relay shim\n#     '''\n#     sensor = GroveRelay(1)\n#     sensor.on()\n\ndef test_turn_relay_off(init_counterfit_device):\n    '''\n    Tests the off method of the Grove relay shim\n    '''\n    sensor = GroveRelay(1)\n    sensor.off()\n"
  },
  {
    "path": "shims/SeeedStudios/grove_py/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/README.md",
    "content": "# CounterFit Shims - Grove\n\n![Grove Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-seeed-python-si114x)](https://pypi.org/project/counterfit-shims-seeed-python-si114x)\n\nShims for the Seeed Grove SI114X sunlight sensor to use with the [CounterFit virtual IoT device app](https://github.com/CounterFit-IoT/CounterFit).\n\nSee the [Seeed Python SI114X Docs](https://github.com/Seeed-Studio/Seeed_Python_SI114X) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate hardware created.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-seeed-python-si114x\n    ```\n\n* Import the Grove modules as normal, but using the `counterfit_shims_seeed_python_si114x` package instead of the `seeed-python-si114x` package, as well as importing the `CounterFitConnection` from the `counterfit_shims_grove.counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    from counterfit_shims_seeed_python_si114x import si114x\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your Grove code as usual. The default assumes you have a light sensor on pin 0, IR on pin 1 and UV on pin 2. You can change these passing additional arguments to the init\n\n    ```python\n    sensor =.si114x()\n    light = sensor.ReadVisible()\n    ir = sensor.ReadIR()\n    uv = sensor.ReadUV()\n    ```\n"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/counterfit_shims_seeed_si114x.py",
    "content": "'''\nThis is the code for\n    - `Grove - Sunlight sensor <https://www.seeedstudio.com/Grove-Sunlight-Sensor.html>`_\n\nExamples:\n\n    .. code-block:: python\n        import time\n        from counterfit_connection import CounterFitConnection\n        import counterfit_shims_seeed_si114x\n\n        # Init the connection to the CounterFit Virtual IoT Device app\n        CounterFitConnection.init('127.0.0.1', 5000)\n\n        light_sensor = seeed_si114x.grove_si114x()\n\n        light = light_sensor.ReadVisible\n'''\n# pylint: disable=too-few-public-methods,unused-argument\n\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['grove_si114x']\n\nclass grove_si114x():\n    '''\n    Class for Grove SI114X\n    Args:\n        light_pin(int): The pin for the light sensor\n        ir_pin(int): The pin for the IR sensor\n        uv_pin(int): The pin for the UV sensor\n    '''\n    def __init__(self, light_pin = 0, ir_pin = 1, uv_pin = 2):\n        self.__light_pin = light_pin\n        self.__ir_pin = ir_pin\n        self.__uv_pin = uv_pin\n\n    @property\n    def ReadVisible(self):\n        '''\n        Read the visible light from the sensor\n        '''\n        return CounterFitConnection.get_sensor_int_value(self.__light_pin)\n\n    @property\n    def ReadIR(self):\n        '''\n        Read the IR light from the sensor\n        '''\n        return CounterFitConnection.get_sensor_int_value(self.__ir_pin)\n\n    @property\n    def ReadUV(self):\n        '''\n        Read the IR light from the sensor\n        '''\n        return CounterFitConnection.get_sensor_int_value(self.__uv_pin)\n"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/setup.py",
    "content": "'''\nShims for the Seeed Python SI144X library for use with the CounterFit Virtual IoT Device app\n'''\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-seeed-python-si114x',\n    py_modules=['counterfit_shims_seeed_python_si114x'],\n    version='0.1.0.dev1',\n    description='Shims for the Seeed Grove SI144X Sunlight sensor for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot grove seeed virtual hardware\",\n    install_requires=['requests','counterfit-connection'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/tests/test_counterfit_shims_seeed_si114x.py",
    "content": "'''\nTests the Grove SI114X.\n\nTo run this test, ensure you have the CounterFit Virtual IoT Device app running, with a light sensor\non pin 0, a IR sensor on pin 1, and a UV sensor on pin 2.\n\nThe light sensor should be set to 500, the IR sensor to 750, and the UV sensor to 1000\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_seeed_si114x import grove_si114x\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_humidity_and_temperature(init_counterfit_device):\n    '''\n    Tests values returned from the SI114X sensor\n    '''\n    sensor = grove_si114x()\n\n    assert sensor.ReadVisible == 500\n    assert sensor.ReadIR == 750\n    assert sensor.ReadUV == 1000\n"
  },
  {
    "path": "shims/SeeedStudios/seeed-python-si114x/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/picamera/.gitignore",
    "content": "test_image*.*"
  },
  {
    "path": "shims/picamera/README.md",
    "content": "# CounterFit Shims - Picamera\n\n![Picamera Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-picamera)](https://pypi.org/project/counterfit-shims-picamera)\n\nShims for the Picamera to read from a virtual camera\n\nSee the [Picamera Docs](https://picamera.readthedocs.io/) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate camera hardware created. Create the camera with a name of `Picamera`.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-picamera\n    ```\n\n* Import Picamera using the `counterfit_shims_picamera` package instead of the `picamera` package, as well as importing the `CounterFitConnection` from the `counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    import counterfit_shims_picamera\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your Picamera code as usual.\n\n    For example, to capture an image as a JPEG:\n\n    ```python\n    camera = PiCamera()\n    image = io.BytesIO()\n    camera.capture(image, 'jpeg')\n    ```\n"
  },
  {
    "path": "shims/picamera/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/picamera/counterfit_shims_picamera/__init__.py",
    "content": "'''\nThe picamera package consists of several modules which provide a pure Python\ninterface to the Raspberry Pi's camera module.\n'''\n\nfrom counterfit_shims_picamera.camera import PiCamera\n"
  },
  {
    "path": "shims/picamera/counterfit_shims_picamera/camera.py",
    "content": "'''\nThe picamera package consists of several modules which provide a pure Python\ninterface to the Raspberry Pi's camera module.\n'''\n# pylint: disable=import-error\nfrom counterfit_connection import CounterFitConnection\nfrom PIL import Image\n\nclass PiCamera():\n    '''\n    Provides a pure Python interface to a virtual Raspberry Pi camera module.\n    '''\n    # pylint: disable=unused-argument\n    def __init__(self, resolution = None, **kwargs):\n        if resolution is not None:\n            self.__width: int = resolution[0]\n            self.__height: int = resolution[1]\n        else:\n            self.__width = -1\n            self.__height = -1\n\n        self.__rotation = 0\n\n    @property\n    def resolution(self):\n        '''\n        Retrieves or sets the resolution at which image captures, video\n        recordings, and previews will be captured.\n        '''\n        return (self.__width, self.__height)\n\n    @resolution.setter\n    def resolution(self, val):\n        '''\n        Retrieves or sets the resolution at which image captures, video\n        recordings, and previews will be captured.\n        '''\n        self.__width = int(val[0])\n        self.__height = int(val[1])\n\n    @property\n    def rotation(self):\n        '''\n        Retrieves or sets the current rotation of the camera's image.\n        '''\n        return self.__rotation\n\n    @rotation.setter\n    def rotation(self, val: int):\n        '''\n        Retrieves or sets the current rotation of the camera's image.\n        '''\n        self.__rotation = val\n\n    def __resize_and_crop(self, image: Image) -> Image:\n        width_adjust =  self.__width / image.size[0]\n        height_adjust = self.__height / image.size[1]\n\n        max_adjust = max([width_adjust, height_adjust])\n\n        new_width = int(image.size[0] * max_adjust)\n        new_height = int(image.size[1] * max_adjust)\n\n        image = image.resize((new_width, new_height))\n\n        left = 0\n        bottom = 0\n\n        if image.size[0] > self.__width:\n            left = int((image.size[0] - self.__width) / 2)\n        if image.size[1] > self.__height:\n            bottom = int((image.size[1] - self.__height) / 2)\n\n        image = image.crop((left, bottom, self.__width + left, self.__height + bottom))\n\n        return image\n\n    def capture(self, output, image_format=None):\n        '''\n        Capture an image from the camera, storing it in *output*.\n        '''\n        # read the image from Counterfit\n        raw_image = CounterFitConnection.read_binary_sensor('Picamera')\n        raw_image.seek(0)\n\n        image = Image.open(raw_image)\n\n        if self.__rotation > 0:\n            image = image.rotate(self.__rotation, expand=True)\n\n        if self.__width > 0 and self.__height > 0:\n            image = self.__resize_and_crop(image)\n\n        image.save(output, format=image_format)\n"
  },
  {
    "path": "shims/picamera/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection\npillow=9.4.0\n"
  },
  {
    "path": "shims/picamera/setup.py",
    "content": "'''\nShims for the Picamera library for use with the CounterFit Virtual IoT Device app\n'''\n# pylint: disable=redefined-builtin\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import find_packages, setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-picamera',\n    packages=find_packages(include=['counterfit_shims_picamera']),\n    version='0.1.0.dev5',\n    description='Shims for the PiCamera library for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot picamera camera virtual hardware\",\n    install_requires=['requests','counterfit-connection','pillow'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/picamera/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/picamera/tests/test_counterfit_shims_picamera.py",
    "content": "'''\nTests the PySerial connection.\n\nTo test this, launch CounterFit, and add 2 GPS sensors, one on /dev/tty0 and one on /dev/tty1.\nSet them both to NMEA data, and the data as hello\\nworld\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nimport counterfit_shims_picamera\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_capture(init_counterfit_device):\n    '''\n    Tests capturing an image\n    '''\n    camera = counterfit_shims_picamera.PiCamera()\n    camera.capture('test_image.png')\n    camera.capture('test_image.jpeg')\n    \ndef test_rotate(init_counterfit_device):\n    '''\n    Tests capturing an image\n    '''\n    camera = counterfit_shims_picamera.PiCamera()\n    camera.rotation = 90\n    camera.capture('test_image_rotate_90.jpeg')\n    \n    camera.rotation = 180\n    camera.capture('test_image_rotate_180.jpeg')\n    \n    camera.rotation = 270\n    camera.capture('test_image_rotate_270.jpeg')\n    \ndef test_resolution_init(init_counterfit_device):\n    '''\n    Tests capturing an image\n    '''\n    camera = counterfit_shims_picamera.PiCamera((1024,768))\n    camera.capture('test_image_resolution_1024_768_init.jpeg')\n    \ndef test_resolution(init_counterfit_device):\n    '''\n    Tests capturing an image\n    '''\n    camera = counterfit_shims_picamera.PiCamera()\n    camera.resolution = (1024,768)\n    camera.capture('test_image_resolution_1024_768.jpeg')\n    \ndef test_resolution_and_rotate(init_counterfit_device):\n    '''\n    Tests capturing an image\n    '''\n    camera = counterfit_shims_picamera.PiCamera()\n    camera.rotation = 90\n    camera.resolution = (1024,768)\n    camera.capture('test_image_rotate_resolution_1024_768.jpeg')\n"
  },
  {
    "path": "shims/picamera/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/pyserial/README.md",
    "content": "# CounterFit Shims - PySerial\n\n![PySerial Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-serial)](https://pypi.org/project/counterfit-shims-serial)\n\nShims for the PySerial to read sensors that use a virtual serial port\n\nSee the [PySerial Docs](https://pyserial.readthedocs.io/en/latest/pyserial.html) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate hardware created.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-serial\n    ```\n\n* Import PySerial using the `counterfit_shims_serial` package instead of the `serial` package, as well as importing the `CounterFitConnection` from the `counterfit_shims_grove.counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    import counterfit_shims_serial\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your PySerial code as usual, setting the port to match the one you set in the CounterFit app.\n\n    For example, create a UART sensor on port `/dev/ttyAMA0`:\n\n    ```python\n    serial = counterfit_shims_serial.Serial('/dev/ttyAMA0', 9600, timeout=1)\n    ```\n\n    The baud and timeout settings are ignored.\n"
  },
  {
    "path": "shims/pyserial/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/pyserial/counterfit_shims_serial.py",
    "content": "# pylint: disable=unused-argument,import-error\n'''Shims for PySerial\n'''\n\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['Serial']\n\nclass Serial():\n    '''Shims for the PySerial Serial class\n    '''\n    def __init__(self, port: str, baud: int = 9600, **kwargs):\n        self.__port = port\n\n    def read(self, **kwargs) -> bytes:\n        '''Reads a character from the serial port\n        '''\n        return CounterFitConnection.read_serial_sensor_char(self.__port).encode('utf-8')\n\n    def readline(self, **kwargs) -> bytes:\n        '''Reads a line from the serial port\n        '''\n        return CounterFitConnection.read_serial_sensor_line(self.__port).encode('utf-8')\n\n    def reset_input_buffer(self):\n        '''Does nothing - here for PySerial compatability\n        '''\n\n    def flush(self):\n        '''Does nothing - here for PySerial compatability\n        '''\n"
  },
  {
    "path": "shims/pyserial/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection\n"
  },
  {
    "path": "shims/pyserial/setup.py",
    "content": "'''\nShims for the PySerial library for use with the CounterFit Virtual IoT Device app\n'''\n# pylint: disable=redefined-builtin\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-serial',\n    py_modules=['counterfit_shims_serial'],\n    version='0.1.0.dev3',\n    description='Shims for the PySerial library for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot pyserial serial virtual hardware\",\n    install_requires=['requests','counterfit-connection'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/pyserial/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/pyserial/tests/test_counterfit_shims_serial.py",
    "content": "'''\nTests the PySerial connection.\n\nTo test this, launch CounterFit, and add 2 GPS sensors, one on /dev/tty0 and one on /dev/tty1.\nSet them both to NMEA data, and the data as hello\\nworld\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nimport counterfit_shims_serial\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_read(init_counterfit_device):\n    '''\n    Tests values returned from the Serial port\n    '''\n    serial = counterfit_shims_serial.Serial('/dev/tty0')\n    assert serial.read().decode('utf-8') == 'h'\n    assert serial.read().decode('utf-8') == 'e'\n    assert serial.read().decode('utf-8') == 'l'\n    assert serial.read().decode('utf-8') == 'l'\n    assert serial.read().decode('utf-8') == 'o'\n    assert serial.read().decode('utf-8') == '\\n'\n    assert serial.read().decode('utf-8') == 'w'\n    assert serial.read().decode('utf-8') == 'o'\n    assert serial.read().decode('utf-8') == 'r'\n    assert serial.read().decode('utf-8') == 'l'\n    assert serial.read().decode('utf-8') == 'd'\n\ndef test_read_line(init_counterfit_device):\n    '''\n    Tests values returned from the Serial port\n    '''\n    serial = counterfit_shims_serial.Serial('/dev/tty1', 9600, timeout=1)\n    assert serial.readline().decode('utf-8') == 'hello'\n    assert serial.readline().decode('utf-8') == 'world'\n"
  },
  {
    "path": "shims/pyserial/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  },
  {
    "path": "shims/rpi_vl53l0x/README.md",
    "content": "# CounterFit Shims - VL53L0X distance sensor\n\n![RPI_VL53L0X Shim](https://img.shields.io/badge/Platform-Python-green) [![PyPI](https://img.shields.io/pypi/v/counterfit-shims-rpi-vl53l0x)](https://pypi.org/project/counterfit-shims-rpi-vl53l0x)\n\nShims for the Rpi-VL53L0X distance sensor.\n\nSee the [Rpi-VL53L0X Docs](https://github.com/turmary/VL53L0X_rasp) for the API documentation.\n\n## Getting started\n\nTo use these shims, you will need to install [CounterFit](https://github.com/CounterFit-IoT/CounterFit) and have it running, with the appropriate hardware created.\n\n* Install this package from pip:\n\n    ```sh\n    pip install counterfit-shims-rpi-vl53l0x\n    ```\n\n* Import VL53L0X using the `counterfit_shims_rpi-vl53l0x` package instead of the `rpi-vl53l0x` package, as well as importing the `CounterFitConnection` from the `counterfit_connection` module:\n\n    ```python\n    from counterfit_connection import CounterFitConnection\n    from counterfit_shims_rpi_vl53l0x.vl53l0x import VL53L0X\n    ```\n\n* Configure the connection to the CounterFit app. Change the hostname and port to where you are running it:\n\n    ```python\n    CounterFitConnection.init('127.0.0.1', 5000)\n    ```\n\n* Write your VL53L0X code as usual, setting the I<sup>2</sup>C address to match the one you set in the CounterFit app.\n\n    For example, create a distance sensor on port `0x29`:\n\n    ```python\n    distance_sensor = VL53L0X(0x29)\n    ```\n\n"
  },
  {
    "path": "shims/rpi_vl53l0x/build.sh",
    "content": "rm ./dist/*\npython3 setup.py bdist_wheel"
  },
  {
    "path": "shims/rpi_vl53l0x/counterfit_shims_rpi_vl53l0x/__init__.py",
    "content": "'''\nThe picamera package consists of several modules which provide a pure Python\ninterface to the Raspberry Pi's camera module.\n'''\n\nfrom counterfit_shims_rpi_vl53l0x.vl53l0x import VL53L0X\n"
  },
  {
    "path": "shims/rpi_vl53l0x/counterfit_shims_rpi_vl53l0x/vl53l0x.py",
    "content": "# pylint: disable=unused-argument,import-error\n'''Shims for PySerial\n'''\n\nfrom counterfit_connection import CounterFitConnection\n\n__all__ = ['VL53L0X']\n\nclass VL53L0X(object):\n    def __init__(self, address:int = 0x29):\n        self.__address = address\n\n    def get_libver(self):\n        return 'VL53L0X for CounterFit'\n\n    def get_devver(self):\n        return 'VL53L0X for CounterFit'\n\n    def begin(self):\n        return 0\n\n    def wait_ready(self):\n        '''\n        return None  -- Error status\n               False -- Timeout\n               True  -- Ready\n        '''\n        return CounterFitConnection.is_connected()\n\n    def get_distance(self):\n        return CounterFitConnection.get_sensor_int_value(self.__address)"
  },
  {
    "path": "shims/rpi_vl53l0x/requirements.txt",
    "content": "wheel=0.38.4\nsetuptools=65.5.0\ntwine=4.0.2\npytest=7.2.2\npytest-runner=6.0.0\nrequests=2.28.2\npylint=2.16.2\ncounterfit-connection\n"
  },
  {
    "path": "shims/rpi_vl53l0x/setup.py",
    "content": "'''\nShims for the rpi_vl53l0x library for use with the CounterFit Virtual IoT Device app\n'''\n# pylint: disable=redefined-builtin\n\nfrom codecs import open\nfrom os import path\nfrom setuptools import find_packages, setup\n\nhere = path.abspath(path.dirname(__file__))\n\nwith open(path.join(here, \"README.md\"), encoding=\"utf-8\") as f:\n    long_description = f.read()\n\nsetup(\n    name='counterfit-shims-rpi-vl53l0x',\n    packages=find_packages(include=['counterfit_shims_rpi_vl53l0x']),\n    version='0.1.0.dev3',\n    description='Shims for the rpi_vl53l0x library for the CounterFit virtual IoT device app',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    author='Jim Bennett',\n    url=\"https://github.com/CounterFit-IoT/CounterFit\",\n    license='MIT',\n    classifiers=[\n        \"Development Status :: 2 - Pre-Alpha\",\n        \"Intended Audience :: Developers\",\n        \"Topic :: Software Development :: Libraries\",\n        \"Topic :: System :: Hardware\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.4\",\n        \"Programming Language :: Python :: 3.5\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\"\n    ],\n    keywords=\"iot rpi_vl53l0x vl53l0x virtual hardware\",\n    install_requires=['requests','counterfit-connection'],\n    setup_requires=['pytest-runner'],\n    tests_require=['pytest==4.4.1'],\n    test_suite='tests',\n)\n"
  },
  {
    "path": "shims/rpi_vl53l0x/tests/__init__.py",
    "content": ""
  },
  {
    "path": "shims/rpi_vl53l0x/tests/test_counterfit_shims_rpi_vl53l0x.py",
    "content": "'''\nTests the RPI VL53L0X connection\n\nCreate a distance sensor at the address 0x29 and set the distance to 100mm\n\n'''\n# pylint: disable=redefined-outer-name,unused-argument\n\nimport pytest\n\nfrom counterfit_connection import CounterFitConnection\nfrom counterfit_shims_rpi_vl53l0x.vl53l0x import VL53L0X\n\n@pytest.fixture\ndef init_counterfit_device():\n    '''\n    Test fixture to initialise the connection to the CounterFit Virtual IoT device running on localhost on port 5000\n    '''\n    CounterFitConnection.init('127.0.0.1', 5000)\n\ndef test_get_distance(init_counterfit_device):\n    '''\n    Tests values returned from the Serial port\n    '''\n    distance_sensor = VL53L0X()\n    distance_sensor.begin()\n\n    if distance_sensor.wait_ready():\n        assert distance_sensor.get_distance() == 100\n\n"
  },
  {
    "path": "shims/rpi_vl53l0x/upload.sh",
    "content": "python3 -m twine upload --repository pypi dist/*"
  }
]