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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ IoTuring/Configurator/configurations.json*
IoTuring/Configurator/dontmoveconf.itg*
.venv
build
*.egg-info
*.egg-info
9 changes: 7 additions & 2 deletions IoTuring/Configurator/MenuPreset.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations

from typing import TypedDict, List
from InquirerPy import inquirer

from IoTuring.Exceptions.Exceptions import UserCancelledException
Expand Down Expand Up @@ -161,6 +161,10 @@ def HasQuestions(self) -> bool:
"""Check if this preset has any questions to ask"""
return bool(self.presets)

class ChoiceDict(TypedDict):
name: str
value: str

def AddEntry(self,
name,
key,
Expand All @@ -169,7 +173,7 @@ def AddEntry(self,
display_if_key_value={},
instruction="",
question_type="text",
choices=[]) -> None:
choices: list[ChoiceDict]=[]) -> None:
"""
Add an entry to the preset with:
- key: the key to use in the dict
Expand All @@ -187,6 +191,7 @@ def AddEntry(self,
- instruction: more text to show
- question_type: text, secret, integer, filepath, select or yesno
- choices: only for select question type
* List of dicts with following keys and values [{name: "name", value: "value"}]
"""

if question_type not in ["text", "secret", "select", "yesno", "integer", "filepath"]:
Expand Down
7 changes: 5 additions & 2 deletions IoTuring/Entity/Deployments/ActiveWindow/ActiveWindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,21 @@ def Update(self):
if self.UpdateSpecificFunction:
self.SetEntitySensorValue(KEY, str(self.UpdateSpecificFunction()))

def GetActiveWindow_macOS(self):
def GetActiveWindow_macOS(self) -> str:
"""Get the active window title on macOS"""
try:
curr_app = NSWorkspace.sharedWorkspace().activeApplication()
curr_app_name = curr_app['NSApplicationName']
return curr_app_name # Better choice beacuse on Mac the window title is a bit buggy
except BaseException:
return "Inactive"

def GetActiveWindow_Windows(self):
def GetActiveWindow_Windows(self) -> str:
"""Get the active window title on Windows"""
return GetWindowText(GetForegroundWindow())

def GetActiveWindow_Linux(self) -> str:
"""Get the active window title on Linux"""
p = self.RunCommand("xprop -root _NET_ACTIVE_WINDOW")

if p.stdout:
Expand Down
6 changes: 4 additions & 2 deletions IoTuring/Entity/Deployments/AppInfo/AppInfo.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import requests
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
Expand Down Expand Up @@ -46,7 +48,7 @@ def Update(self):
self.SetEntitySensorExtraAttribute(KEY_UPDATE, EXTRA_ATTRIBUTE_UPDATE_ERROR, GET_UPDATE_ERROR_MESSAGE)


def GetUpdateInformation(self):
def GetUpdateInformation(self) -> str | bool:
"""
Get the update information of IoTuring
Returns False if no update is available
Expand All @@ -70,7 +72,7 @@ def GetUpdateInformation(self):
else:
raise UpdateCheckException()

def versionToInt(version: str):
def versionToInt(version: str) -> int:
return int(''.join([i for i in version if i.isdigit()]))

class UpdateCheckException(Exception):
Expand Down
9 changes: 7 additions & 2 deletions IoTuring/Entity/Deployments/Battery/Battery.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import annotations
from typing import TypedDict, Dict
import psutil
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
Expand All @@ -7,6 +8,10 @@
KEY_PERCENTAGE = 'percentage'
KEY_CHARGING_STATUS = 'charging'

class BatteryInformation(TypedDict):
level: int
charging: bool

class Battery(Entity):
NAME = "Battery"
supports_charge = False
Expand Down Expand Up @@ -34,9 +39,9 @@ def Update(self):
self.SetEntitySensorValue(
KEY_CHARGING_STATUS, str(batteryInfo['charging']))

def GetBatteryInformation(self) -> dict:
def GetBatteryInformation(self) -> BatteryInformation:
battery = psutil.sensors_battery()
if not battery:
raise Exception("No battery sensor for this host")

return {'level': battery.percent, 'charging': battery.power_plugged}
return BatteryInformation({'level': battery.percent, 'charging': battery.power_plugged})
3 changes: 2 additions & 1 deletion IoTuring/Entity/Deployments/FileSwitch/FileSwitch.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from paho.mqtt.client import MQTTMessage

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
Expand All @@ -25,7 +26,7 @@ def Initialize(self):
self.RegisterEntityCommand(EntityCommand(
self, KEY_CMD, self.Callback, KEY_STATE))

def Callback(self, message):
def Callback(self, message: MQTTMessage):
payloadString = message.payload.decode('utf-8')

if payloadString == "True":
Expand Down
2 changes: 1 addition & 1 deletion IoTuring/Entity/Deployments/Hostname/Hostname.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ def Initialize(self):
# The value for this sensor is static for the entire script run time
self.SetEntitySensorValue(KEY_HOSTNAME, self.GetHostname())

def GetHostname(self):
def GetHostname(self) -> str:
return socket.gethostname()
3 changes: 2 additions & 1 deletion IoTuring/Entity/Deployments/Monitor/Monitor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import ctypes
import re
from paho.mqtt.client import MQTTMessage

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
Expand All @@ -26,7 +27,7 @@ def Initialize(self):
self.RegisterEntityCommand(EntityCommand(
self, KEY_CMD, self.Callback))

def Callback(self, message):
def Callback(self, message: MQTTMessage):
payloadString = message.payload.decode('utf-8')

if payloadString == STATE_ON:
Expand Down
3 changes: 2 additions & 1 deletion IoTuring/Entity/Deployments/Notify/Notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import os
import json
from paho.mqtt.client import MQTTMessage

supports_win = True
try:
Expand Down Expand Up @@ -82,7 +83,7 @@ def Initialize(self):

self.RegisterEntityCommand(EntityCommand(self, KEY, self.Callback))

def Callback(self, message):
def Callback(self, message: MQTTMessage):
if self.data_mode == MODE_DATA_VIA_PAYLOAD:
# Get data from payload:
payloadString = message.payload.decode('utf-8')
Expand Down
4 changes: 3 additions & 1 deletion IoTuring/Entity/Deployments/Power/Power.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from paho.mqtt.client import MQTTMessage

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand
from IoTuring.Exceptions.Exceptions import UnknownConfigKeyException
Expand Down Expand Up @@ -64,7 +66,7 @@ def Initialize(self):
self.RegisterEntityCommand(EntityCommand(
self, command_key, self.Callback))

def Callback(self, message):
def Callback(self, message: MQTTMessage):
# From the topic we can find the command:
key = message.topic.split("/")[-1]
self.RunCommand(
Expand Down
40 changes: 23 additions & 17 deletions IoTuring/Entity/Deployments/Temperature/Temperature.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
from __future__ import annotations

import psutil

from typing import List
from psutil._common import shwtemp

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
from IoTuring.Entity.ValueFormat import ValueFormatter, ValueFormatterOptions
Expand Down Expand Up @@ -120,7 +126,7 @@ def UpdateLinux(self):

index += 1

def packageNameToEntitySensorKey(self, packageName):
def packageNameToEntitySensorKey(self, packageName) -> str:
return KEY_SENSOR_FORMAT.format(packageName)


Expand All @@ -130,7 +136,7 @@ def CheckSystemSupport(cls):
raise cls.UnsupportedOsException()

class psutilTemperaturePackage():
def __init__(self, packageName, packageData) -> None:
def __init__(self, packageName: str, packageData: List[shwtemp]) -> None:
""" packageData is the value of the the dict returned by psutil.sensors_temperatures() """
self.packageName = packageName
self.sensors = []
Expand All @@ -144,52 +150,52 @@ def getSensors(self) -> list:
return self.sensors.copy()

# Package stats strategies here: my choice is to return always the highest among the temperatures, critical: here will return the lowest
def getCurrent(self):
def getCurrent(self) -> float | None:
""" Returns highest current temperature among this package sensors. None if any sensor has that data. """
highest = None
for sensor in self.getSensors():
if sensor.hasCurrent() and (highest == None or highest < sensor.getCurrent()):
highest = sensor.getCurrent()
highest = sensor.getCurrent()
return highest

def getHighest(self):
def getHighest(self) -> float | None:
""" Returns highest highest temperature among this package sensors. None if any sensor has that data. """
highest = None
for sensor in self.getSensors():
if sensor.hasHighest() and (highest == None or highest < sensor.getHighest()):
highest = sensor.getHighest()
return highest

def getCritical(self):
def getCritical(self) -> float | None:
""" Returns lower critical temperature among this package sensors. None if any sensor has that data. """
lowest = None
for sensor in self.getSensors():
if sensor.hasCritical() and (lowest == None or lowest > sensor.getCritical()):
lowest = sensor.getCritical()
return lowest

def hasCurrent(self):
def hasCurrent(self) -> bool:
""" True if at least a sensor of the package has the current property """
for sensor in self.sensors:
if sensor.hasCurrent():
return True
return False

def hasHighest(self):
def hasHighest(self) -> bool:
""" True if at least a sensor of the package has the highest property """
for sensor in self.sensors:
if sensor.hasHighest():
return True
return False

def hasCritical(self):
def hasCritical(self) -> bool:
""" True if at least a sensor of the package has the critical property """
for sensor in self.sensors:
if sensor.hasCritical():
return True
return False

def getAttributesDict(self):
def getAttributesDict(self) -> dict:
attributes = {}
for index, sensor in enumerate(self.getSensors()):
if sensor.hasLabel():
Expand All @@ -213,30 +219,30 @@ def __init__(self, sensorData) -> None:
self.highest = sensorData[2]
self.critical = sensorData[3]

def getCurrent(self):
def getCurrent(self) -> float | None:
return self.current

def getLabel(self) -> str:
return self.label

def getHighest(self):
def getHighest(self) -> float | None:
return self.highest

def getCritical(self):
def getCritical(self) -> float | None:
return self.critical

def hasLabel(self):
def hasLabel(self) -> bool:
""" True if a label is set for this sensor """
return not self.label == None and not self.label.strip() == ""

def hasCurrent(self):
def hasCurrent(self) -> bool:
""" True if a current is set for this sensor """
return not self.current == None

def hasHighest(self):
def hasHighest(self) -> bool:
""" True if a highest is set for this sensor """
return not self.highest == None

def hasCritical(self):
def hasCritical(self) -> bool:
""" True if a critical is set for this sensor """
return not self.critical == None
3 changes: 2 additions & 1 deletion IoTuring/Entity/Deployments/Terminal/Terminal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
from IoTuring.Logger.consts import STATE_OFF, STATE_ON
from IoTuring.Entity.ValueFormat import ValueFormatterOptions
from paho.mqtt.client import MQTTMessage
import re

KEY = "terminal"
Expand Down Expand Up @@ -186,7 +187,7 @@ def Initialize(self):
self.state = ""
self.state_message = ""

def Callback(self, message):
def Callback(self, message: MQTTMessage):

# Get data from payload:
payloadString = message.payload.decode('utf-8')
Expand Down
2 changes: 1 addition & 1 deletion IoTuring/Entity/Deployments/Time/Time.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ def Initialize(self):
def Update(self):
self.SetEntitySensorValue(KEY_NOW, self.GetCurrentTime())

def GetCurrentTime(self):
def GetCurrentTime(self) -> str:
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
2 changes: 1 addition & 1 deletion IoTuring/Entity/Deployments/Username/Username.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def Initialize(self):
self.SetEntitySensorValue(KEY_USERNAME, self.GetUsername())


def GetUsername(self):
def GetUsername(self) -> str:
# Gives user's home directory
userhome = os.path.expanduser('~')

Expand Down
6 changes: 4 additions & 2 deletions IoTuring/Entity/Deployments/Volume/Volume.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import re
from typing import List
from paho.mqtt.client import MQTTMessage

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
Expand Down Expand Up @@ -64,7 +66,7 @@ def Update(self):
self.SetEntitySensorExtraAttribute(
KEY_STATE, EXTRA_KEY_MUTED_OUTPUT, output_muted)

def Callback(self, message):
def Callback(self, message: MQTTMessage):
payloadString = message.payload.decode('utf-8')

# parse the payload and get the volume number which is between 0 and 100
Expand All @@ -85,7 +87,7 @@ def UpdateMac(self):
# result like: output volume:44, input volume:89, alert volume:100, output muted:false
command = self.RunCommand(
command=['osascript', '-e', 'get volume settings'])
result = command.stdout.strip().split(',')
result: List[str] | None = command.stdout.strip().split(',')

output_volume = result[0].split(':')[1]
input_volume = result[1].split(':')[1]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import yaml
import re
import time
from paho.mqtt.client import MQTTMessage
from typing import Callable

from IoTuring.Configurator.MenuPreset import MenuPreset
Expand Down Expand Up @@ -301,7 +302,7 @@ def GetConnectedSensor(self) -> HomeAssistantSensor | None:

def GenerateCommandCallback(self) -> Callable:
""" Generate the callback function """
def CommandCallback(message):
def CommandCallback(message: MQTTMessage):
status = self.entityCommand.CallCallback(message)
if status and self.wh.client.IsConnected():
if self.connected_sensor:
Expand Down
2 changes: 2 additions & 0 deletions tests/Entity/Deployments/Temperature/test_Temperature.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from IoTuring.Entity.Deployments.Temperature.Temperature import psutilTemperaturePackage, psutilTemperatureSensor

class TestPsutilTemperatureSensor:
Expand Down