Skip to content

Commit

Permalink
Merge branch 'develop' into frequencycounter
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjiU authored Jul 31, 2024
2 parents 17d77f8 + e040590 commit 90fc21d
Show file tree
Hide file tree
Showing 37 changed files with 496 additions and 408 deletions.
19 changes: 14 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: [3.7, 3.8]
python-version: [3.8]

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -85,19 +85,28 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- id: branch-name
uses: tj-actions/[email protected]
- name: Ensure we can checkout gh-pages
- name: Get branch names
id: branch-names
uses: tj-actions/branch-names@v8
- name: Ensure we can checkout gh-pages for release (${{ steps.branch-names.outputs.tag }})
if: steps.branch-names.outputs.is_tag == 'true'
run: |
git checkout gh-pages
git checkout ${{ steps.branch-name.outputs.current_branch }}
git checkout ${{ steps.branch-names.outputs.tag }}
- name: Ensure we can checkout gh-pages for pr (${{ steps.branch-names.outputs.current_branch }})
if: steps.branch-names.outputs.is_tag == 'false'
run: |
git checkout gh-pages
git checkout ${{ steps.branch-names.outputs.current_branch }}
- name: Setup Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install dependencies and generate docs
run: |
pip install poetry
ls
git status
poetry install
poetry run python docs_src/generate_docs.py
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@ Unreleased
==========
- Nothing!

.v2.4.0 - 2024-07-20
====================
- Bump tj-actions/branch-names from 2.2 to 7.0.7 in /.github/workflows by @dependabot in https://github.com/flyte/mqtt-io/pull/339
- # Fix for poetry/docutils related bug by @BenjiU in https://github.com/flyte/mqtt-io/pull/367
- upgrade DHT11/DHT22 backing library by @pansila in https://github.com/flyte/mqtt-io/pull/297
- Install gcc for slim docker to build rpi.gpio on demand by @BenjiU in https://github.com/flyte/mqtt-io/pull/368
- Remove lint warnings from bmp085.py by @BenjiU in https://github.com/flyte/mqtt-io/pull/375
- Add support for YF-S201 flow rate sensor by @linucks in https://github.com/flyte/mqtt-io/pull/370
- Support for ENS160 digital multi-gas sensor with multiple IAQ data (TVOC, eCO2, AQI) by @linucks in https://github.com/flyte/mqtt-io/pull/371
- feat: add MH-Z19 sensor module by @kleest in https://github.com/flyte/mqtt-io/pull/365
- Add Support for Sunxi Linux Boards by @fabys77 in https://github.com/flyte/mqtt-io/pull/100

.v2.3.0 - 2024-03-01
====================
- 324 pinned pyyaml version incompatible with latest cython 300 by @BenjiU in #325
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Hardware support is provided by specific GPIO, Sensor and Stream modules. It's e
- SHT40/SHT41/SHT45 temperature and humidity sensors (`sht4x`)
- YF-S201 flow rate sensor (`yfs201`)
- FREQUENCYCOUNTER Counts pulses from GPIOs and return the frequency in Hz (frequencycounter)
- FLOWSENSOR generic flow rate sensor like YF-S201 or YF-DN50 (`flowsensor`)


### Streams

Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Top level of MQTT IO package.
"""

VERSION = "2.3.0"
VERSION = "2.5.1"
10 changes: 5 additions & 5 deletions mqtt_io/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ def custom_validate_main_config(config: ConfigType) -> ConfigType:
bad_configs: Dict[str, Dict[str, List[str]]] = {}

# Make sure each of the IO configs refer to an existing module config
module_and_io_sections = dict(
gpio_modules=("digital_inputs", "digital_outputs"),
sensor_modules=("sensor_inputs",),
stream_modules=("stream_reads", "stream_writes"),
)
module_and_io_sections = {
"gpio_modules": ("digital_inputs", "digital_outputs"),
"sensor_modules": ("sensor_inputs",),
"stream_modules": ("stream_reads", "stream_writes"),
}
for module_section, io_sections in module_and_io_sections.items():
validate_gpio_module_names(bad_configs, config, module_section, io_sections)

Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/config/config.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ mqtt:
description: MQTT Client implementation module path.
extra_info: |
There's currently only one implementation, which uses the
[asyncio-mqtt](https://github.com/sbtinstruments/asyncio-mqtt/) client.
[aiomqtt](https://github.com/sbtinstruments/aiomqtt/) client.
type: string
required: no
default: mqtt_io.mqtt.asyncio_mqtt
default: mqtt_io.mqtt.aiomqtt
ha_discovery:
type: dict
required: no
Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
STREAM_TOPIC = "stream"

MODULE_IMPORT_PATH = "mqtt_io.modules"
MODULE_CLASS_NAMES = dict(gpio="GPIO", sensor="Sensor", stream="Stream")
MODULE_CLASS_NAMES = {"gpio": 'GPIO', "sensor": 'Sensor', "stream": 'Stream'}

MQTT_SUB_PRIORITY = 1
MQTT_ANNOUNCE_PRIORITY = 2
Expand Down
58 changes: 29 additions & 29 deletions mqtt_io/home_assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,21 @@ def get_common_config(
Return config that's common across all HQ discovery announcements.
"""
disco_conf: ConfigType = mqtt_conf["ha_discovery"]
config = dict(name=io_conf["name"])
config = {"name": io_conf['name']}
config.update(
dict(
availability_topic="/".join(
{
"availability_topic": '/'.join(
(mqtt_conf["topic_prefix"], mqtt_conf["status_topic"])
),
payload_available=mqtt_conf["status_payload_running"],
payload_not_available=mqtt_conf["status_payload_dead"],
device=dict(
manufacturer="MQTT IO",
model=f"v{VERSION}",
identifiers=[mqtt_options.client_id],
name=disco_conf["name"],
),
)
"payload_available": mqtt_conf["status_payload_running"],
"payload_not_available": mqtt_conf["status_payload_dead"],
"device": {
"manufacturer": 'MQTT IO',
"model": f'v{VERSION}',
"identifiers": [mqtt_options.client_id],
"name": disco_conf["name"],
},
}
)
config.update(io_conf.get("ha_discovery", {}))
return config
Expand All @@ -54,12 +54,12 @@ def hass_announce_digital_input(
disco_prefix: str = disco_conf["prefix"]
sensor_config = get_common_config(in_conf, mqtt_conf, mqtt_options)
sensor_config.update(
dict(
unique_id=f"{mqtt_options.client_id}_{in_conf['module']}_input_{name}",
state_topic="/".join((mqtt_conf["topic_prefix"], INPUT_TOPIC, name)),
payload_on=in_conf["on_payload"],
payload_off=in_conf["off_payload"],
)
{
"unique_id": f'{mqtt_options.client_id}_{in_conf["module"]}_input_{name}',
"state_topic": '/'.join((mqtt_conf["topic_prefix"], INPUT_TOPIC, name)),
"payload_on": in_conf["on_payload"],
"payload_off": in_conf["off_payload"],
}
)
return MQTTMessageSend(
"/".join(
Expand Down Expand Up @@ -90,13 +90,13 @@ def hass_announce_digital_output(
disco_prefix: str = disco_conf["prefix"]
switch_config = get_common_config(out_conf, mqtt_conf, mqtt_options)
switch_config.update(
dict(
unique_id=f"{mqtt_options.client_id}_{out_conf['module']}_output_{name}",
state_topic="/".join((prefix, OUTPUT_TOPIC, name)),
command_topic="/".join((prefix, OUTPUT_TOPIC, name, SET_SUFFIX)),
payload_on=out_conf["on_payload"],
payload_off=out_conf["off_payload"],
)
{
"unique_id": f'{mqtt_options.client_id}_{out_conf["module"]}_output_{name}',
"state_topic": '/'.join((prefix, OUTPUT_TOPIC, name)),
"command_topic": '/'.join((prefix, OUTPUT_TOPIC, name, SET_SUFFIX)),
"payload_on": out_conf["on_payload"],
"payload_off": out_conf["off_payload"],
}
)
return MQTTMessageSend(
"/".join(
Expand Down Expand Up @@ -127,10 +127,10 @@ def hass_announce_sensor_input(
disco_prefix: str = disco_conf["prefix"]
sensor_config = get_common_config(sens_conf, mqtt_conf, mqtt_options)
sensor_config.update(
dict(
unique_id=f"{mqtt_options.client_id}_{sens_conf['module']}_sensor_{name}",
state_topic="/".join((prefix, SENSOR_TOPIC, name)),
)
{
"unique_id": f'{mqtt_options.client_id}_{sens_conf["module"]}_sensor_{name}',
"state_topic": '/'.join((prefix, SENSOR_TOPIC, name)),
}
)
if "expire_after" not in sensor_config:
sensor_config["expire_after"] = sens_conf["interval"] * 2 + 5
Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/modules/gpio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import asyncio
import logging
from concurrent.futures import ThreadPoolExecutor
from enum import Enum, Flag, auto
from enum import Enum, Flag, IntFlag, auto
from typing import Any, Callable, Dict, Iterable, List, Optional

from ...types import ConfigType, PinType
Expand Down Expand Up @@ -48,7 +48,7 @@ class InterruptEdge(Enum):
BOTH = auto()


class InterruptSupport(Flag):
class InterruptSupport(IntFlag):
"""
Classifies the kind of support a GPIO module has for interrupts.
Expand Down
2 changes: 1 addition & 1 deletion mqtt_io/modules/gpio/mcp23017.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class GPIO(GenericGPIO):
| InterruptSupport.SET_TRIGGERS
)
PIN_SCHEMA = {
"pin": dict(type="integer", required=True, min=0, max=15),
"pin": {"type": 'integer', "required": True, "min": 0, "max": 15},
}

def setup_module(self) -> None:
Expand Down
4 changes: 2 additions & 2 deletions mqtt_io/modules/gpio/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from . import GenericGPIO, InterruptEdge, InterruptSupport, PinDirection, PinPUD

REQUIREMENTS = ()
CONFIG_SCHEMA = dict(test=dict(type="boolean", required=False, default=False))
CONFIG_SCHEMA = {"test": {"type": 'boolean', "required": False, "default": False}}


# pylint: disable=useless-super-delegation,too-many-instance-attributes
Expand All @@ -24,7 +24,7 @@ class GPIO(GenericGPIO):
| InterruptSupport.CAPTURE_REGISTER
)
PIN_SCHEMA = {
"test": dict(type="boolean", required=False, default=False),
"test": {"type": 'boolean', "required": False, "default": False},
}

def __init__(self, config: ConfigType):
Expand Down
57 changes: 57 additions & 0 deletions mqtt_io/modules/gpio/sunxi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
Sunxi Board
"""
from typing import Optional
from ...types import ConfigType, PinType
from . import GenericGPIO, PinDirection, PinPUD

REQUIREMENTS = ("pySUNXI",)

class GPIO(GenericGPIO):
"""
Implementation of GPIO class for Sunxi native GPIO.
"""
def setup_module(self) -> None:
# pylint: disable=import-outside-toplevel,import-error
from pySUNXI import gpio # type: ignore

self.io: gpio = gpio
self.direction_map = {PinDirection.INPUT: gpio.INPUT, PinDirection.OUTPUT: gpio.OUTPUT}

self.pullup_map = {
PinPUD.OFF: gpio.PULLNONE,
PinPUD.UP: gpio.PULLUP,
PinPUD.DOWN: gpio.PULLDOWN,
}
gpio.init()

def setup_pin(
self,
pin: PinType,
direction: PinDirection,
pullup: PinPUD,
pin_config: ConfigType,
initial: Optional[str] = None,
) -> None:
direction = self.direction_map[direction]

if pullup is None:
pullup = self.pullup_map[PinPUD.OFF]
else:
pullup = self.pullup_map[pullup]

initial_state = {None: -1, "low": 0, "high": 1}[initial]
#self.io.setup(pin, direction, pull_up_down=pullup, initial=initial)
self.io.setcfg(pin, direction)
self.io.pullup(pin, pullup)
if direction==self.io.OUTPUT:
self.io.output(pin, initial_state)

def set_pin(self, pin: PinType, value: bool) -> None:
self.io.output(pin, value)

def get_pin(self, pin: PinType) -> bool:
return bool(self.io.input(pin))

def cleanup(self) -> None:
pass
59 changes: 27 additions & 32 deletions mqtt_io/modules/sensor/ads1x15.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,15 @@

REQUIREMENTS = ("adafruit-circuitpython-ads1x15",)
CONFIG_SCHEMA: CerberusSchemaType = {
"chip_addr": dict(type="integer", required=False, empty=False, default=0x48),
"type": dict(
type="string",
required=True,
empty=False,
allowed=SENSOR_TYPES,
),
"pins": dict(type="list", required=True, empty=False, allowed=[0, 1, 2, 3]),
"gain": dict(
required=False,
empty=False,
allowed=[0.6666666666666666, 1, 2, 4, 8, 16],
default=1,
),
"chip_addr": {"type": 'integer', "required": False, "empty": False, "default": 0x48},
"type": {"type": 'string', "required": True, "empty": False, "allowed": SENSOR_TYPES},
"pins": {"type": 'list', "required": True, "empty": False, "allowed": [0, 1, 2, 3]},
"gain": {
"required": False,
"empty": False,
"allowed": [0.6666666666666666, 1, 2, 4, 8, 16],
"default": 1
},
}


Expand All @@ -37,20 +32,20 @@ class Sensor(GenericSensor):
"""

SENSOR_SCHEMA: CerberusSchemaType = {
"type": dict(
type="string",
required=False,
empty=False,
allowed=["value", "voltage"],
default="value",
),
"pin": dict(
type="integer",
required=True,
empty=False,
allowed=[0, 1, 2, 3],
default=0,
),
"type": {
"type": 'string',
"required": False,
"empty": False,
"allowed": ['value', 'voltage'],
"default": 'value',
},
"pin": {
"type": 'integer',
"required": True,
"empty": False,
"allowed": [0, 1, 2, 3],
"default": 0,
},
}

def setup_module(self) -> None:
Expand Down Expand Up @@ -90,10 +85,10 @@ def get_value(self, sens_conf: ConfigType) -> SensorValueType:
# acquire the lock
with self.lock:
sens_type = sens_conf["type"]
data = dict(
value=self.channels[sens_conf["pin"]].value,
voltage=self.channels[sens_conf["pin"]].voltage,
)
data = {
"value": self.channels[sens_conf['pin']].value,
"voltage": self.channels[sens_conf['pin']].voltage,
}

return cast(
float,
Expand Down
Loading

0 comments on commit 90fc21d

Please sign in to comment.