Skip to content

Commit

Permalink
Merge pull request #37 from infeeeee/monitor-switch
Browse files Browse the repository at this point in the history
Monitor as switch, linked command with sensor, battery fix
  • Loading branch information
richibrics authored Jan 26, 2023
2 parents 9c4a933 + b88c040 commit f9cc9ac
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 51 deletions.
11 changes: 8 additions & 3 deletions IoTuring/Entity/Deployments/Battery/Battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ class Battery(Entity):

def Initialize(self):
self.RegisterEntitySensor(EntitySensor(self, KEY_PERCENTAGE))
self.RegisterEntitySensor(EntitySensor(self, KEY_CHARGING_STATUS))

# Check if charging state working:
batteryInfo = self.GetBatteryInformation()
if isinstance(batteryInfo['charging'], bool):
self.RegisterEntitySensor(EntitySensor(self, KEY_CHARGING_STATUS))

def PostInitialize(self):
# Check if battery infomration are present
Expand All @@ -23,8 +27,9 @@ def Update(self):
batteryInfo = self.GetBatteryInformation()
self.SetEntitySensorValue(KEY_PERCENTAGE, int(
batteryInfo['level']), ValueFormatter.Options(ValueFormatter.TYPE_PERCENTAGE))
self.SetEntitySensorValue(
KEY_CHARGING_STATUS, str(batteryInfo['charging']))
if isinstance(batteryInfo['charging'], bool):
self.SetEntitySensorValue(
KEY_CHARGING_STATUS, str(batteryInfo['charging']))

def GetBatteryInformation(self):
battery = psutil.sensors_battery()
Expand Down
55 changes: 55 additions & 0 deletions IoTuring/Entity/Deployments/FileSwitch/FileSwitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from pathlib import Path

from IoTuring.Entity.Entity import Entity
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
from IoTuring.Configurator.MenuPreset import MenuPreset

KEY_STATE = 'fileswitch_state'
KEY_CMD = 'fileswitch'

CONFIG_KEY_PATH = 'path'


class FileSwitch(Entity):
NAME = "FileSwitch"
ALLOW_MULTI_INSTANCE = True

def Initialize(self):

try:
self.config_path = self.GetConfigurations()[CONFIG_KEY_PATH]
except Exception as e:
raise Exception("Configuration error: " + str(e))

self.RegisterEntitySensor(EntitySensor(self, KEY_STATE, True))
self.RegisterEntityCommand(EntityCommand(
self, KEY_CMD, self.Callback, KEY_STATE))

def PostInitialize(self):
pass

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

if payloadString == "True":
Path(self.config_path).touch()

elif payloadString == "False":
Path(self.config_path).unlink(missing_ok=True)

else:
raise Exception('Incorrect payload!')

def Update(self):
self.SetEntitySensorValue(KEY_STATE,
str(Path(self.config_path).exists()))

extra = {}
extra["Path"] = str(self.config_path)
self.SetEntitySensorExtraAttributes(KEY_STATE, extra)

@classmethod
def ConfigurationPreset(self):
preset = MenuPreset()
preset.AddEntry("Path to file?", CONFIG_KEY_PATH, mandatory=True)
return preset
76 changes: 53 additions & 23 deletions IoTuring/Entity/Deployments/Monitor/Monitor.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import subprocess
import ctypes
import os as sys_os
import os
import re

from IoTuring.Entity.Entity import Entity
from ctypes import *
from IoTuring.Entity.EntityData import EntityCommand, EntitySensor
from IoTuring.Entity import consts
from IoTuring.Logger.consts import STATE_OFF, STATE_ON

from IoTuring.Entity.EntityData import EntityCommand

KEY_TURN_ALL_OFF = 'turn_all_off'
KEY_TURN_ALL_ON = 'turn_all_on'
KEY_STATE = 'monitor_state'
KEY_CMD = 'monitor'


class Monitor(Entity):
Expand All @@ -19,27 +22,54 @@ def Initialize(self):

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

supports_linux = False
if self.os == consts.OS_FIXED_VALUE_LINUX:
# Check if xset is working:
p = subprocess.run(
['xset', 'dpms'], capture_output=True, shell=False)
if p.stderr:
raise Exception(f"Xset dpms error: {p.stderr.decode()}")
elif not os.getenv('DISPLAY'):
raise Exception('No $DISPLAY environment variable!')
else:
supports_linux = True

if self.os == 'Windows' or (self.os == 'Linux' and sys_os.environ.get('DISPLAY')):
if self.os == consts.OS_FIXED_VALUE_WINDOWS:
self.RegisterEntityCommand(EntityCommand(
self, KEY_TURN_ALL_OFF, self.CallbackTurnAllOff))
self, KEY_CMD, self.Callback))
elif supports_linux:
# Support for sending state on linux
self.RegisterEntitySensor(EntitySensor(self, KEY_STATE))
self.RegisterEntityCommand(EntityCommand(
self, KEY_TURN_ALL_ON, self.CallbackTurnAllOn))

def CallbackTurnAllOff(self, message):
if self.os == 'Windows':
ctypes.windll.user32.SendMessageA(0xFFFF, 0x0112, 0xF170, 2)
elif self.os == 'Linux':
# Check if X11 or something else
if sys_os.environ.get('DISPLAY'):
command = 'xset dpms force off'
subprocess.Popen(command.split(), stdout=subprocess.PIPE)
self, KEY_CMD, self.Callback, KEY_STATE))

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

def CallbackTurnAllOn(self, message):
if self.os == 'Windows':
ctypes.windll.user32.SendMessageA(0xFFFF, 0x0112, 0xF170, -1)
elif self.os == 'Linux':
# Check if X11 or something else
if sys_os.environ.get('DISPLAY'):
if payloadString == STATE_ON:
if self.os == consts.OS_FIXED_VALUE_WINDOWS:
ctypes.windll.user32.SendMessageA(0xFFFF, 0x0112, 0xF170, -1)
elif self.os == consts.OS_FIXED_VALUE_LINUX:
command = 'xset dpms force on'
subprocess.Popen(command.split(), stdout=subprocess.PIPE)

elif payloadString == STATE_OFF:
if self.os == consts.OS_FIXED_VALUE_WINDOWS:
ctypes.windll.user32.SendMessageA(0xFFFF, 0x0112, 0xF170, 2)
elif self.os == consts.OS_FIXED_VALUE_LINUX:
command = 'xset dpms force off'
subprocess.Popen(command.split(), stdout=subprocess.PIPE)
else:
raise Exception('Incorrect payload!')

def Update(self):
if self.os == consts.OS_FIXED_VALUE_LINUX:
p = subprocess.run(['xset', 'q'], capture_output=True, shell=False)
outputString = p.stdout.decode()
monitorState = re.findall(
'Monitor is (.{2,3})', outputString)[0].upper()
if monitorState in [STATE_OFF, STATE_ON]:
self.SetEntitySensorValue(KEY_STATE, monitorState)
else:
raise Exception(f'Incorrect monitor state: {monitorState}')
24 changes: 22 additions & 2 deletions IoTuring/Entity/EntityData.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ def __init__(self, entity, key):
self.entityId = entity.GetEntityId()
self.id = self.entityId + "." + key
self.key = key
self.entity = entity

def GetEntity(self):
return self.entity

def GetId(self):
return self.id
Expand Down Expand Up @@ -62,17 +66,33 @@ def SetExtraAttributes(self, _dict):

class EntityCommand(EntityData):

def __init__(self, entity, key, callbackFunction):
def __init__(self, entity, key, callbackFunction, connectedEntitySensorKey = None):
"""
If a key for the entity sensor is passed, warehouses that support it use this command as a switch with state.
Better to register the sensor before this command to avoud unexpected behaviours.
"""
EntityData.__init__(self, entity, key)
self.callbackFunction = callbackFunction
self.connectedEntitySensorKey = connectedEntitySensorKey

def SupportsState(self):
return self.connectedEntitySensorKey is not None

def GetConnectedEntitySensor(self):
""" Returns the entity sensor connected to this command, if this command supports state.
Otherwise returns None. """
return self.GetEntity().GetEntitySensorByKey(self.connectedEntitySensorKey)

def CallCallback(self, message):
""" Safely run callback for this command, passing the message (a paho.mqtt.client.MQTTMessage) """
""" Safely run callback for this command, passing the message (a paho.mqtt.client.MQTTMessage).
Reutrns True if callback was run correctly, False if an error occurred."""
self.Log(self.LOG_DEBUG, "Callback")
try:
self.RunCallback(message)
return True
except Exception as e:
self.Log(self.LOG_ERROR, "Error while running callback: " + str(e))
return False

def RunCallback(self, message):
""" Called only by CallCallback.
Expand Down
4 changes: 4 additions & 0 deletions IoTuring/Logger/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
LOG_DEVELOPMENT = 5


# On/off states as strings:
STATE_ON = "ON"
STATE_OFF = "OFF"

# Fill start of string with spaces to jusitfy the message (0: no padding)
# First for type, second for source
STRINGS_LENGTH = [8, 30]
Expand Down
Loading

0 comments on commit f9cc9ac

Please sign in to comment.