Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependencies system #57

Merged
merged 11 commits into from
May 18, 2023
77 changes: 4 additions & 73 deletions IoTuring/Configurator/Configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,8 @@ def ManageSingleEntity(self, entityConfig, ecm: EntityClassManager):
choice = input("Select an operation: ")

if choice == "r" or choice == "R":
# get dependencies errors: if no one, okay; print those with error func otherwise.
deps = self.CheckDependencies_AbleToRemove(
entityConfig[KEY_ENTITY_TYPE], ecm)
if(len(deps) == 0):
if(Configurator.ConfirmQuestion()):
self.RemoveActiveEntity(entityConfig, ecm)
else:
self.PrintDependencyError_RemoveEntity(deps)
if(Configurator.ConfirmQuestion()):
self.RemoveActiveEntity(entityConfig, ecm)
elif choice == "e" or choice == "E":
self.EditActiveEntity(entityConfig, ecm)

Expand Down Expand Up @@ -251,14 +245,8 @@ def SelectNewEntity(self, ecm: EntityClassManager):
choice = int(choice) # If not valid I have a ValueError
choice = choice - 1 # So now chosen entity = active entity in configurations
if choice >= 0 and choice < len(entityList):
# get dependencies error if available: or okay or print those with its function
deps = self.CheckDependencies_AbleToActivate(
entityList[choice], ecm)
if(len(deps) == 0):
# WIll also open the configuration menu
self.AddActiveEntity(entityList[choice], ecm)
else:
self.PrintDependencyError_ActivateEntity(deps)
# WIll also open the configuration menu
self.AddActiveEntity(entityList[choice], ecm)
choice = True
else:
raise ValueError()
Expand Down Expand Up @@ -302,20 +290,6 @@ def IsEntityActive(self, entityName) -> bool:

def RemoveActiveEntity(self, entityConfig, ecm: EntityClassManager) -> None:
""" Remove entity name from the list of active entities if present """
# TODO Check if no dependencies from it

for activeEntity in self.GetConfigurations()[KEY_ACTIVE_ENTITIES]:
if activeEntity != entityConfig:
# Get class from entity type
entityClass = ecm.GetClassFromName(
activeEntity[KEY_ENTITY_TYPE])
# Retrieve entity dependencies
deps = entityClass.GetDependenciesList()
# Check if entity I want to remove is dependent from the entity I'm iterating
if entityConfig[KEY_ENTITY_TYPE] in deps:
raise Exception("Can't remove this entity because " +
activeEntity[KEY_ENTITY_TYPE] + " depends from it :(")

if entityConfig in self.config[KEY_ACTIVE_ENTITIES]:
self.config[KEY_ACTIVE_ENTITIES].remove(entityConfig)

Expand Down Expand Up @@ -382,49 +356,6 @@ def EntityMenuPresetToConfiguration(self, entityName, preset) -> None:
self.config[KEY_ACTIVE_ENTITIES].append(_dict)
print("Configuration added for \""+entityName+"\" :)")

def CheckDependencies_AbleToActivate(self, entityToActivate, entityClassManager: EntityClassManager):
""" Return list of entities depends on. """
entityClass = entityClassManager.GetClassFromName(entityToActivate)
dependingOn = entityClass.GetDependenciesList()
for dependency in entityClass.GetDependenciesList():
for active_entity in self.GetConfigurations()[KEY_ACTIVE_ENTITIES]:
if dependency == active_entity[KEY_ENTITY_TYPE]:
dependingOn.remove(dependency)
return dependingOn

def CheckDependencies_AbleToRemove(self, entityToDisable, entityClassManager: EntityClassManager):
""" Return list of entities depend on this. """
# Each entity has a dependency list. Check if active entities do not rely on this.
dependingOnThis = []
for activeEntity in self.GetConfigurations()[KEY_ACTIVE_ENTITIES]:
# List of dependencies is in the class: load the class
entityClass = entityClassManager.GetClassFromName(
activeEntity[KEY_ENTITY_TYPE])
for dependency in entityClass.GetDependenciesList():
if dependency == entityToDisable:
dependingOnThis.append(activeEntity[KEY_ENTITY_TYPE])
return dependingOnThis

def PrintDependencyError_ActivateEntity(self, dependencies):
""" Prints a message with the dependencies the user has to activate before activating this entity """

print("!!! You can't activate this Entity. Please activate the following entities in order to use this one: !!!")

for dependency in dependencies:
print("---> " + dependency + " <----")

print("End of dependencies list")

def PrintDependencyError_RemoveEntity(self, dependencies):
""" Prints a message with the dependencies the user has to remove before removing this entity """

print("!!! You can't remove this Entity. Please remove the following entities in order to remove this one: !!!")

for dependency in dependencies:
print("---> " + dependency + " <----")

print("End of dependencies list")

def PrintSeparator(self):
print("\n"+SEPARATOR_CHAR_NUMBER*'#')

Expand Down
41 changes: 21 additions & 20 deletions IoTuring/Entity/Deployments/ActiveWindow/ActiveWindow.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,57 @@
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
from IoTuring.Entity import consts
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD
from IoTuring.MyApp.SystemConsts import DesktopEnvironmentDetection as De


# Linux dep
try:
import os
import re
import sys
from subprocess import PIPE, Popen
linux_support = True
except:
except BaseException:
linux_support = False


# Windows dep
try:
from win32gui import GetWindowText, GetForegroundWindow
windows_support = True
except:
except BaseException:
windows_support = False

# macOS dep (in PyObjC)
# macOS dep
try:
from AppKit import NSWorkspace
macos_support = True
except:
except BaseException:
macos_support = False


KEY = 'active_window'


class ActiveWindow(Entity):
NAME = "ActiveWindow"
DEPENDENCIES = ["Os"]

def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY))

def PostInitialize(self):
os = self.GetDependentEntitySensorValue("Os", "operating_system")
# Specific function for this os/de, set this here to avoid all OS filters on Update
# Specific function for this os/de, set this here to avoid all OS
# filters on Update
self.UpdateSpecificFunction = None

if os == consts.OS_FIXED_VALUE_LINUX:
if linux_support:
if OsD.IsLinux():
if De.IsWayland():
raise Exception("Wayland is not supported")
elif linux_support:
self.UpdateSpecificFunction = self.GetActiveWindow_Linux
else:
raise Exception("Unsatisfied dependencies for this entity")
elif os == consts.OS_FIXED_VALUE_WINDOWS:
elif OsD.IsWindows():
if windows_support:
self.UpdateSpecificFunction = self.GetActiveWindow_Windows
else:
raise Exception("Unsatisfied dependencies for this entity")
elif os == consts.OS_FIXED_VALUE_MACOS:
elif OsD.IsMacos():
if macos_support:
self.UpdateSpecificFunction = self.GetActiveWindow_macOS
else:
Expand All @@ -62,6 +60,9 @@ def PostInitialize(self):
raise Exception(
'Entity not available for this operating system')

if self.UpdateSpecificFunction:
self.RegisterEntitySensor(EntitySensor(self, KEY))

def Update(self):
self.SetEntitySensorValue(KEY, str(self.UpdateSpecificFunction()))

Expand All @@ -70,7 +71,7 @@ def GetActiveWindow_macOS(self):
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:
except BaseException:
return "Inactive"

def GetActiveWindow_Windows(self):
Expand All @@ -80,14 +81,14 @@ def GetActiveWindow_Linux(self):
root = Popen(['xprop', '-root', '_NET_ACTIVE_WINDOW'], stdout=PIPE)
stdout, stderr = root.communicate()

m = re.search(b'^_NET_ACTIVE_WINDOW.* ([\w]+)$', stdout)
m = re.search(b'^_NET_ACTIVE_WINDOW.* ([\\w]+)$', stdout)

if m is not None:
window_id = m.group(1)
window = Popen(['xprop', '-id', window_id, 'WM_NAME'], stdout=PIPE)
stdout, stderr = window.communicate()

match = re.match(b'WM_NAME\(\w+\) = (?P<name>.+)$', stdout)
match = re.match(b'WM_NAME\\(\\w+\\) = (?P<name>.+)$', stdout)
if match is not None:
return match.group('name').decode('UTF-8').strip('"')

Expand Down
1 change: 0 additions & 1 deletion IoTuring/Entity/Deployments/AppInfo/AppInfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY_VERSION))
self.RegisterEntitySensor(EntitySensor(self, KEY_UPDATE, supportsExtraAttributes=True))

def PostInitialize(self):
self.SetEntitySensorValue(KEY_NAME, App.getName())
self.SetEntitySensorValue(KEY_VERSION, App.getVersion())
self.SetUpdateTimeout(600)
Expand Down
3 changes: 1 addition & 2 deletions IoTuring/Entity/Deployments/Battery/Battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,9 @@ def Initialize(self):
if isinstance(batteryInfo['charging'], bool):
self.RegisterEntitySensor(EntitySensor(self, KEY_CHARGING_STATUS))

def PostInitialize(self):
# Check if battery information are present
if not psutil.sensors_battery():
raise("No battery sensor for this host")
raise Exception("No battery sensor for this host")

def Update(self):
batteryInfo = self.GetBatteryInformation()
Expand Down
2 changes: 1 addition & 1 deletion IoTuring/Entity/Deployments/Cpu/settings.yaml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file used anywhere? I can't find any lines opening it, maybe this just remained from PyMonitorMQTT?

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
requirements:
sensors:
- Os:
- O:
dont_send: True

discovery:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
from IoTuring.MyApp.SystemConsts import DesktopEnvironmentDetection as De
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD

KEY_DE = 'desktop_environment'
EXTRA_KEY_WAYLAND = 'wayland'

# TODO Here I need the possibility for fixed value -> a configuration

Expand All @@ -11,15 +13,15 @@ class DesktopEnvironment(Entity):
NAME = "DesktopEnvironment"

def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY_DE))
# The value for this sensor is static for the entire script run time (set in initialize so other entities can get the value from Postinitialize)
self.SetEntitySensorValue(KEY_DE, self.GetDesktopEnvironment())

# If value passed use it else get it from the system
def GetDesktopEnvironment(self):
# Attribute only on Linux
self.RegisterEntitySensor(EntitySensor(
self, KEY_DE, supportsExtraAttributes=OsD.IsLinux()))

de = os.environ.get('DESKTOP_SESSION')
if de == None:
de = "base"
# The value for this sensor is static for the entire script run time
self.SetEntitySensorValue(KEY_DE, De.GetDesktopEnvironment())

return de
# Add an attribute on linux checking if it's a wayland session:
if OsD.IsLinux():
self.SetEntitySensorExtraAttribute(
KEY_DE, EXTRA_KEY_WAYLAND, str(De.IsWayland()))
7 changes: 2 additions & 5 deletions IoTuring/Entity/Deployments/Disk/Disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,22 @@
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntitySensor
from IoTuring.Entity.ValueFormat import ValueFormatter, ValueFormatterOptions
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD

KEY_USED_PERCENTAGE = 'space_used_percentage'


class Disk(Entity):
NAME = "Disk"
DEPENDENCIES = ["Os"]

def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY_USED_PERCENTAGE, valueFormatterOptions=ValueFormatterOptions(ValueFormatterOptions.TYPE_PERCENTAGE)))

def PostInitialize(self):
self.os = self.GetDependentEntitySensorValue('Os', "operating_system")

def Update(self):
self.SetEntitySensorValue(KEY_USED_PERCENTAGE, self.GetDiskUsedPercentage())

def GetDiskUsedPercentage(self):
if self.os == 'macOS':
if OsD.IsMacos():
return psutil.disk_usage('/Users')[3] # macos has a different disk structure
else:
return psutil.disk_usage('/')[3]
11 changes: 3 additions & 8 deletions IoTuring/Entity/Deployments/DisplayMode/DisplayMode.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os as sys_os
from IoTuring.Entity.Entity import Entity
from ctypes import *
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD

from IoTuring.Entity.EntityData import EntityCommand

Expand All @@ -13,21 +14,15 @@

class DisplayMode(Entity):
NAME = "DisplayMode"
DEPENDENCIES = ["Os"]

def Initialize(self):
pass

def PostInitialize(self):
self.os = self.GetDependentEntitySensorValue('Os', "operating_system")

callback = None
if self.os == 'Windows':
if OsD.IsWindows():
sr = sys_os.environ.get('SystemRoot')
if sys_os.path.exists('{}\System32\DisplaySwitch.exe'.format(sr)):
callback = self.Callback_Win
else:
self.Log(self.LOG_ERROR, "Error log:\nOperating system: {}, sr: {}, path exists: {}".format(self.os, sr, sys_os.path.exists('{}\System32\DisplaySwitch.exe'.format(sr))))
self.Log(self.LOG_ERROR, "Error log:\nOperating system: {}, sr: {}, path exists: {}".format(OsD.GetOs(), sr, sys_os.path.exists('{}\System32\DisplaySwitch.exe'.format(sr))))
raise Exception("Unsupported software, report this log to the developer")
else:
raise Exception("Unsupported operating system for this entity")
Expand Down
3 changes: 0 additions & 3 deletions IoTuring/Entity/Deployments/FileSwitch/FileSwitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ def Initialize(self):
self.RegisterEntityCommand(EntityCommand(
self, KEY_CMD, self.Callback, KEY_STATE))

def PostInitialize(self):
pass

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

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 @@ -10,7 +10,7 @@ class Hostname(Entity):

def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY_HOSTNAME))
# The value for this sensor is static for the entire script run time (set in initialize so other entities can get the value from Postinitialize)
# The value for this sensor is static for the entire script run time
self.SetEntitySensorValue(KEY_HOSTNAME, self.GetHostname())

def GetHostname(self):
Expand Down
9 changes: 4 additions & 5 deletions IoTuring/Entity/Deployments/Lock/Lock.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import subprocess
from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand
from IoTuring.MyApp.SystemConsts import DesktopEnvironmentDetection as De
from IoTuring.MyApp.SystemConsts import OperatingSystemDetection as OsD # don't name Os as could be a problem with old configurations that used the Os entity

KEY_LOCK = 'lock'

Expand All @@ -21,16 +23,13 @@

class Lock(Entity):
NAME = "Lock"
DEPENDENCIES = ["Os", "DesktopEnvironment"]

def Initialize(self):
self.RegisterEntityCommand(EntityCommand(
self, KEY_LOCK, self.Callback_Lock))

def PostInitialize(self):
self.os = self.GetDependentEntitySensorValue('Os', "operating_system")
self.de = self.GetDependentEntitySensorValue(
'DesktopEnvironment', 'desktop_environment')
self.os = OsD.GetOs()
self.de = De.GetDesktopEnvironment()

def Callback_Lock(self, message):
if self.os in commands:
Expand Down
Loading