Skip to content

Commit

Permalink
Merge pull request #123 from borisbu/develop
Browse files Browse the repository at this point in the history
Releasing 2.2.3
  • Loading branch information
RobinTail authored Jul 28, 2023
2 parents d736945 + 38f4d18 commit 39bb21b
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 62 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: "CodeQL"

on:
push:
branches: [ "master", "develop" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master", "develop" ]

jobs:
analyze:
name: Analyze
runs-on: 'ubuntu-latest'
timeout-minutes: 360
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality


# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Version 2

### 2.2.3

- Refactoring.
- Using `f"string"` syntax instead of `"".format()`.
- Strict handling of booleans.

### 2.2.2

- Another technical update.
Expand Down
91 changes: 44 additions & 47 deletions octoprint_octorelay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ def on_after_startup(self):

for index in RELAY_INDEXES:
settings[index].update(self._settings.get([index]))
self._logger.debug("settings for {}: {}".format(index, settings[index]))
self._logger.debug(f"settings for {index}: {settings[index]}")

if settings[index]['active']:
relay_pin = int(settings[index]['relay_pin'])
initial_value = settings[index]['initial_value']
inverted_output = settings[index]['inverted_output']
initial_value = bool(settings[index]['initial_value'])
inverted_output = bool(settings[index]['inverted_output'])

# Setting the default state of pin
GPIO.setup(relay_pin, GPIO.OUT)
# XOR with inverted
GPIO.output(relay_pin, initial_value != inverted_output)
GPIO.output(relay_pin, initial_value is not inverted_output)

self.update_ui()
self.polling_timer = RepeatedTimer(POLLING_INTERVAL, self.input_polling, daemon=True)
Expand Down Expand Up @@ -92,7 +92,7 @@ def has_switch_permission(self):
return False

def on_api_command(self, command, data):
self._logger.debug("on_api_command {}, some_parameter is {}".format(command, data))
self._logger.debug(f"on_api_command {command}, parameters {data}")

# API command to get relay statuses
if command == LIST_ALL_COMMAND:
Expand All @@ -102,12 +102,12 @@ def on_api_command(self, command, data):
settings = self._settings.get([index], merged=True)
if settings["active"]:
relay_pin = int(settings["relay_pin"])
inverted = settings['inverted_output']
inverted = bool(settings['inverted_output'])
GPIO.setup(relay_pin, GPIO.OUT)
relayData = {
"id": index,
"name": settings["labelText"],
"active": inverted != GPIO.input(relay_pin),
"active": inverted is not bool(GPIO.input(relay_pin)),
}
activeRelays.append(relayData)
return flask.jsonify(activeRelays)
Expand All @@ -116,10 +116,10 @@ def on_api_command(self, command, data):
if command == GET_STATUS_COMMAND:
settings = self._settings.get([data["pin"]], merged=True)
relay_pin = int(settings["relay_pin"])
inverted = settings['inverted_output']
inverted = bool(settings['inverted_output'])
GPIO.setwarnings(False)
GPIO.setup(relay_pin, GPIO.OUT)
relayState = inverted != GPIO.input(relay_pin)
relayState = inverted is not bool(GPIO.input(relay_pin))
return flask.jsonify(status=relayState)

if command == UPDATE_COMMAND:
Expand All @@ -133,39 +133,33 @@ def update_relay(self, index):
settings = self._settings.get([index], merged=True)

relay_pin = int(settings["relay_pin"])
inverted = settings['inverted_output']
inverted = bool(settings['inverted_output'])
cmdON = settings['cmdON']
cmdOFF = settings['cmdOFF']

GPIO.setwarnings(False)

GPIO.setup(relay_pin, GPIO.OUT)
# XOR with inverted
relayState = inverted != GPIO.input(relay_pin)
relayState = inverted is not bool(GPIO.input(relay_pin))

self._logger.debug("OctoRelay before pin: {}, inverted: {}, relayState: {}".format(
relay_pin,
inverted,
relayState
))
self._logger.debug(f"OctoRelay before pin: {relay_pin}, inverted: {inverted}, relayState: {relayState}")

# toggle state
relayState = not relayState

GPIO.setup(relay_pin, GPIO.OUT)
# XOR with inverted
GPIO.output(relay_pin, inverted != relayState)
GPIO.output(relay_pin, inverted is not relayState)

GPIO.setwarnings(True)
if relayState:
if cmdON:
self._logger.info(
"OctoRelay system command: {}".format(cmdON))
self._logger.info(f"OctoRelay system command: {cmdON}")
os.system(cmdON)
else:
if cmdOFF:
self._logger.info(
"OctoRelay system command: {}".format(cmdOFF))
self._logger.info(f"OctoRelay system command: {cmdOFF}")
os.system(cmdOFF)
self.update_ui()
return "ok"
Expand All @@ -174,7 +168,7 @@ def update_relay(self, index):
return "error"

def on_event(self, event, payload):
self._logger.debug("Got event: {}".format(event))
self._logger.debug(f"Got event: {event}")
if event == Events.CLIENT_OPENED:
self.update_ui()
elif event == Events.PRINT_STARTED:
Expand All @@ -197,19 +191,19 @@ def print_started(self):
for off_timer in self.turn_off_timers:
try:
self.turn_off_timers[off_timer].cancel()
self._logger.info("cancelled timer: {}".format(off_timer))
self._logger.info(f"cancelled timer: {off_timer}")
except Exception:
self._logger.warn("could not cancel timer: {}".format(off_timer))
self._logger.warn(f"could not cancel timer: {off_timer}")
for index in RELAY_INDEXES:
settings = self._settings.get([index], merged=True)

relay_pin = int(settings["relay_pin"])
inverted = settings['inverted_output']
autoONforPrint = settings['autoONforPrint']
inverted = bool(settings['inverted_output'])
autoONforPrint = bool(settings['autoONforPrint'])
cmdON = settings['cmdON']
active = settings["active"]
active = bool(settings["active"])
if autoONforPrint and active:
self._logger.debug("turning on pin: {}, index: {}".format(relay_pin, index))
self._logger.debug(f"turning on pin: {relay_pin}, index: {index}")
self.turn_on_pin(relay_pin, inverted, cmdON)
self.update_ui()

Expand All @@ -218,65 +212,64 @@ def print_stopped(self):
settings = self._settings.get([index], merged=True)

relay_pin = int(settings["relay_pin"])
inverted = settings['inverted_output']
autoOFFforPrint = settings['autoOFFforPrint']
inverted = bool(settings['inverted_output'])
autoOFFforPrint = bool(settings['autoOFFforPrint'])
autoOffDelay = int(settings['autoOffDelay'])
cmdOFF = settings['cmdOFF']
active = settings["active"]
active = bool(settings["active"])
if autoOFFforPrint and active:
self._logger.debug("turn off pin: {} in {} seconds. index: {}".format(
relay_pin, autoOffDelay, index))
self._logger.debug(f"turn off pin: {relay_pin} in {autoOffDelay} seconds. index: {index}")
self.turn_off_timers[index] = ResettableTimer(
autoOffDelay, self.turn_off_pin, [relay_pin, inverted, cmdOFF])
self.turn_off_timers[index].start()
self.update_ui()

def turn_off_pin(self, relay_pin, inverted, cmdOFF):
def turn_off_pin(self, relay_pin: int, inverted: bool, cmdOFF):
GPIO.setup(relay_pin, GPIO.OUT)
# XOR with inverted
GPIO.output(relay_pin, inverted != False)
GPIO.output(relay_pin, inverted is not False)
GPIO.setwarnings(True)
if cmdOFF:
os.system(cmdOFF)
self._logger.info("pin: {} turned off".format(relay_pin))
self._logger.info(f"pin: {relay_pin} turned off")
self.update_ui()

def turn_on_pin(self, relay_pin, inverted, cmdON):
def turn_on_pin(self, relay_pin: int, inverted: bool, cmdON):
GPIO.setup(relay_pin, GPIO.OUT)
# XOR with inverted
GPIO.output(relay_pin, inverted == False)
GPIO.output(relay_pin, inverted is False)
GPIO.setwarnings(True)
if cmdON:
os.system(cmdON)
self._logger.info("pin: {} turned on".format(relay_pin))
self._logger.info(f"pin: {relay_pin} turned on")

def update_ui(self):
settings = DEFAULT_SETTINGS.copy()
for index in RELAY_INDEXES:
settings[index].update(self._settings.get([index]))

labelText = settings[index]["labelText"]
active = int(settings[index]["active"])
active = int(settings[index]["active"]) # todo make it bool later
relay_pin = int(settings[index]["relay_pin"])
inverted = settings[index]['inverted_output']
inverted = bool(settings[index]['inverted_output'])
iconOn = settings[index]['iconOn']
iconOff = settings[index]['iconOff']
confirmOff = settings[index]['confirmOff']
confirmOff = bool(settings[index]['confirmOff'])

# set the icon state
GPIO.setup(relay_pin, GPIO.OUT)
self.model[index]['relay_pin'] = relay_pin
self.model[index]['state'] = GPIO.input(relay_pin)
self.model[index]['state'] = GPIO.input(relay_pin) # int
self.model[index]['labelText'] = labelText
self.model[index]['active'] = active
if inverted != self.model[index]['state']:
if inverted is not bool(self.model[index]['state']):
self.model[index]['iconText'] = iconOn
self.model[index]['confirmOff'] = confirmOff
else:
self.model[index]['iconText'] = iconOff
self.model[index]['confirmOff'] = False

#self._logger.info("update ui with model {}".format(self.model))
#self._logger.info(f"update ui with model {self.model}")
self._plugin_manager.send_plugin_message(self._identifier, self.model)

def process_at_command(self, comm_instance, phase, command, parameters, tags=None, *args, **kwargs):
Expand All @@ -299,8 +292,12 @@ def get_update_information(self):
def input_polling(self):
self._logger.debug("input_polling")
for index in RELAY_INDEXES:
if self.model[index]['active'] and GPIO.input(self.model[index]['relay_pin']) != self.model[index]['state']:
self._logger.debug("relay: {} has changed its pin state".format(index))
# model::active is currently int, see update_ui()
isActive = bool(self.model[index]['active'])
modelState = self.model[index]['state'] # int
actualState = GPIO.input(self.model[index]['relay_pin']) # int
if isActive and actualState != modelState:
self._logger.debug(f"relay: {index} has changed its pin state")
self.update_ui()
break

Expand Down
4 changes: 1 addition & 3 deletions octoprint_octorelay/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@
UPDATES_CONFIG = {
**GITHUB,
"type": "github_release",
"pip": "https://github.com/{}/{}/archive/{}.zip".format(
GITHUB["user"], GITHUB["repo"], "{target}"
),
"pip": f"https://github.com/{GITHUB['user']}/{GITHUB['repo']}/archive/{{target}}.zip",
"stable_branch": STABLE_CHANNEL,
"prerelease_branches": [ PRE_RELEASE_CHANNEL ]
}
Expand Down
22 changes: 11 additions & 11 deletions octoprint_octorelay/test__init.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_constructor(self):
# During the instantiation should configure GPIO and set initial values to certain props
GPIO_mock.setmode.assert_called_with("MockedBCM")
GPIO_mock.setwarnings.assert_called_with(False)
self.assertEqual(self.plugin_instance.polling_timer, None)
self.assertIsNone(self.plugin_instance.polling_timer)
self.assertEqual(self.plugin_instance.turn_off_timers, {})
self.assertEqual(self.plugin_instance.model, {
"r1": {}, "r2": {}, "r3": {}, "r4": {},
Expand Down Expand Up @@ -246,18 +246,18 @@ def test_input_polling(self):
# First active relay having state not equal to the one stored in model should trigger UI update
self.plugin_instance.update_ui = Mock()
self.plugin_instance.model = {
"r1": { "active": False, "relay_pin": 4, "state": True },
"r2": { "active": True, "relay_pin": 17, "state": True },
"r3": { "active": True, "relay_pin": 18, "state": False }
"r1": { "active": 0, "relay_pin": 4, "state": 1 },
"r2": { "active": 1, "relay_pin": 17, "state": 1 },
"r3": { "active": 1, "relay_pin": 18, "state": 0 }
}
GPIO_mock.input = Mock(return_value=True)
GPIO_mock.input = Mock(return_value=1)
self.plugin_instance.input_polling()
self.plugin_instance.update_ui.assert_called_with()
self.plugin_instance._logger.debug.assert_called_with("relay: r3 has changed its pin state")

def test_update_ui(self):
# Should send message via plugin manager containing actual settings and the pins state
GPIO_mock.input = Mock(return_value=False)
GPIO_mock.input = Mock(return_value=0)
cases = [
{ "inverted": True, "expectedIcon": "ON" },
{ "inverted": False, "expectedIcon": "OFF" }
Expand All @@ -277,9 +277,9 @@ def test_update_ui(self):
for index in self.plugin_instance.get_settings_defaults():
expectedModel[index] = {
"relay_pin": 17,
"state": False,
"state": 0,
"labelText": "TEST",
"active": True,
"active": 1,
"iconText": case["expectedIcon"],
"confirmOff": False
}
Expand Down Expand Up @@ -458,14 +458,14 @@ def test_has_switch_permission(self):
permissionsMock.PLUGIN_OCTORELAY_SWITCH.can = case["mock"]
actual = self.plugin_instance.has_switch_permission()
permissionsMock.PLUGIN_OCTORELAY_SWITCH.can.assert_called_with()
self.assertEqual(actual, case["expected"])
self.assertIs(actual, case["expected"])

@patch('flask.jsonify')
@patch('os.system')
def test_on_api_command(self, jsonMock, systemMock):
# Depending on command should perform different actions and response with JSON
self.plugin_instance.update_ui = Mock()
GPIO_mock.input = Mock(return_value=True)
GPIO_mock.input = Mock(return_value=1)
cases = [
{
"command": "listAllStatus",
Expand Down Expand Up @@ -576,7 +576,7 @@ def test_process_at_command(self):
self.plugin_instance.update_relay = Mock()
actual = self.plugin_instance.process_at_command(None, None, "OCTORELAY", "r4")
self.plugin_instance.update_relay.assert_called_with("r4")
self.assertEqual(actual, None)
self.assertIsNone(actual)

def test_get_additional_permissions(self):
expected = [{
Expand Down
Loading

0 comments on commit 39bb21b

Please sign in to comment.