Migrating to Moterey & python 3.1 - iterating class instance

Posted on
Sun Jul 24, 2022 11:55 am
RedYuriPrime offline
Posts: 58
Joined: Oct 14, 2014
Location: Oxford

Migrating to Moterey & python 3.1 - iterating class instance

I have legitimate python 3.1 code (e.g. it runs in ST3 under python 3.1) which won't run under indigo 2022.1 under Monterey.
For the heating controls set up a class Room, creat instances for each room with all the relevant heating parameters, and then iterate through these instances to control the heating. This worked fine under indigo 2021.1 and earlier in python 2.7.

In python 3.1 it has not been as eay to find a neat way of iterationg through instances of a class, but the code below shows one way, which runs under 3.1 in ST3. This builds a class level list of the names of the rooms as astrings, and then uses this list to gnerate the iteration, converting the string names of the rooms to the actual class instances using the sys call:
identifier = getattr(sys.modules[__name__], strName)

However this won't run in Indigo. Am I missing something - is there some reason why the sys library returns different values because of the way code modules run within indigo?
Does anyone have a better method of iterating through instances of a class that does work under Indigo 2022.1?

Thanks in advance

Geoff

PYTHON CODE:

Code: Select all
import time
import sys

"""########################################################################
Class name: Room
Function: This class handles all routines associated with room heating controls
########################################################################"""


class Room():
    roomInstances = []

    def __getitem__(self, key):
        return self.__getattribute__(key)

    def __setitem__(self, key, value):
        self.__setattr__(key, value)
        return "OK"

    def __init__(self,name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,roomTempSensor,roomThermostat = "",demandFactor = 1, radOffset = 0):
        self.name = name
        self.floor = floor
        self.WeekDay = WeekDay
        self.WeekEnd = WeekEnd
        self.Office = Office
        self.Empty = Empty
        self.Fixed = Fixed
        self.defaultMode = defaultMode
        self.radiators = radiators
        self.heaters = heaters
        self.roomTempSensor = roomTempSensor
        self.roomThermostat = roomThermostat
        self.roomTempSensor = roomTempSensor
        self.demandFactor = demandFactor
        self.radOffset = radOffset
        self.__class__.roomInstances.append(self.name)
        #  logPrint(0, "Room load - " + self.name)
        logPrint(0, "Room instances in list: " + ', '.join(Room.roomInstances))


def logPrint(level, logStr):
    # log levels 0-5, 0 suppresses everything, 1 is standard operation, 2-5 are debugging
    print("%H% " + str(0) + " " + logStr)
    return


def str_to_instance(strName, className):
    try:
        identifier = getattr(sys.modules[__name__], strName)
    except AttributeError:
        raise NameError("%s doesn't exist." % strName)
    if isinstance(identifier, className):
        return identifier
    raise TypeError("%s is not an instance of class %t" % strName, className)


# *** Room Definitions

#Lounge
name = "Lounge"
floor = "0"
WeekDay = ['00:00',17,'08:00',19,'10:00',20,'16:00',21,'22:30',21,'23:00',17]
WeekEnd = ['00:00',17,'08:00',19,'10:00',20,'16:00',21,'22:30',21,'23:59',17]
Office = WeekDay
Empty = ['00:00',17,'08:00',19,'10:00',19,'16:00',19,'22:30',19,'23:00',17]
Fixed = ['00:00',20]
User1 = ['00:00',20]
User2 = ['00:00',20]
defaultMode = 'Normal'
days = ['Mon','WeekDay','Tue','WeekDay','Wed','WeekDay','Thu','WeekDay','Fri','WeekEnd','Sat','WeekEnd','Sun','WeekEnd']
radiators = ['RadStat Lounge Main','RadStat Lounge Left Window','RadStat Lounge Right Window']
heaters = []
Lounge = Room(name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,roomTempSensor="Lounge - T",roomThermostat="RoomStat Lounge",demandFactor=2,radOffset=2.5)

#Dining Room
name="DiningRoom"
floor ="0"
WeekDay = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30', 21,'23:00',17]
WeekEnd = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30', 21,'23:59',17]
Office = WeekDay
Empty = ['00:00',17,'06:30',19,'10:00',19,'16:00',19,'22:30', 19,'23:00',17]
Fixed = ['00:00',20]
User1 = ['00:00',20]
User2 = ['00:00',20]
defaultMode = 'Normal'
days = ['Mon','WeekDay','Tue','WeekDay','Wed','WeekDay','Thu','WeekDay','Fri','WeekEnd','Sat','WeekEnd','Sun','WeekEnd']
radiators = ['RadStat Dining Room']
heaters = []
DiningRoom = Room(name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,roomTempSensor="Dining Room - T",roomThermostat="",demandFactor=0.5,radOffset=1)

#Kitchen
name="Kitchen"
floor ="0"
WeekDay = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30',21,'23:00',17]
WeekEnd = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30',21,'23:30',17]
Office = WeekDay
Empty = ['00:00',17,'06:30',19,'10:00',19,'16:00',19,'22:30',19,'23:00',17]
Fixed = ['00:00',20]
User1 = ['00:00',20]
User2 = ['00:00',20]
defaultMode = 'Normal'
days = ['Mon','WeekDay','Tue','WeekDay','Wed','WeekDay','Thu','WeekDay','Fri','WeekEnd','Sat','WeekEnd','Sun','WeekEnd']
radiators = ['RadStat Kitchen','RadStat Utility Room']
heaters = []
Kitchen = Room(name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,roomTempSensor="Kitchen - T",roomThermostat="",demandFactor=1,radOffset=-1)


"""########################################################################
MAIN ROUTINE
########################################################################"""


for roomName in Room.roomInstances:
    roomID = str_to_instance(roomName, Room)
    logPrint(0, "Room -  " + roomName)
    logPrint(0, "RoomName -  " + str(roomID.name))

Z-wave system with 80+ devices, Indigo7 on a dedicated MacMini
Controlling lighting, heating, hotwater, security; but still a lot to learn ... :shock:

Posted on
Sun Jul 24, 2022 1:11 pm
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Migrating to Moterey & python 3.1 - iterating class inst

Please edit that to use the CODE tags for readability.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Sun Jul 24, 2022 1:17 pm
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Migrating to Moterey & python 3.1 - iterating class inst

Also, can you explain WHY you're using this convoluted method to maintain a list of room instances?

I think the difference is that ST is pure Python, and Indigo runs the Python modules from a C++ host process. So the sys.* calls are running in a slightly different environment.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Mon Jul 25, 2022 9:21 am
jay (support) offline
Site Admin
User avatar
Posts: 18221
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Migrating to Moterey & python 3.1 - iterating class inst

I have to say, after looking at this code for quite a while, I agree with @FlyingDiver - seems like a very un-pythonic way of managing a list of room instances. Seems to me a much better way would be to just create a list, then add each new instance you create to the list. No magic needed for class lookups or whatever. Then you just iterate over the list. So:

  1. Define the Room class (no need for roomInstances since the class should only define the Room object, not track instances of itself, that's pretty un-pythonic).
  2. Instantiate a standard python list object which will track instances of room objects
  3. Create each Room instance, then add those to the list created in #2
  4. Iterate over the list created in #2 to perform whatever operations you need on each room
Seems like a very straight-forward implementation. If somehow something that's not a Room instance gets added to the list, then it will raise an exception when you try to use it. You can catch that exception, log the appropriate message, then continue. Note, using print in a script that's running from Indigo will do nothing. You'll want to use indigo.server.log instead so that it'll show up in the Event Log window. You don't need your logPrint method either. If you really want, you can use isinstance() to make sure that each object in the list is an instance of Room.

Now, it's possible that I'm not completely understanding what this code is supposed to be doing. If that's the case, then you'll need to get more specific about how this code is used, from where, etc.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jul 25, 2022 9:47 am
FlyingDiver offline
User avatar
Posts: 7222
Joined: Jun 07, 2014
Location: Southwest Florida, USA

Re: Migrating to Moterey & python 3.1 - iterating class inst

If you want to be able to look up the Room instance by name, you could use a dict to store the instances with the name as key. You would have to ensure you never have duplicate names.

joe (aka FlyingDiver)
my plugins: http://forums.indigodomo.com/viewforum.php?f=177

Posted on
Mon Jul 25, 2022 10:43 am
jay (support) offline
Site Admin
User avatar
Posts: 18221
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Migrating to Moterey & python 3.1 - iterating class inst

You could also use a list comprehension to find all elements that have the same name, which given your data would return a list with just the one match or an empty list if there were no matches:

Code: Select all
matches = [room for room in roomInstances if room.name == "Kitchen"]

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Mon Jul 25, 2022 11:22 am
jay (support) offline
Site Admin
User avatar
Posts: 18221
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Migrating to Moterey & python 3.1 - iterating class inst

I decided to make this a teachable moment. I've updated the script to be (mostly) PEP8 standard (those are standards for the formatting of python scripts), fixed a few issues, and added comments/suggestions. It must be run from Indigo since we're using the indigo.server.log method to print results.

Code: Select all
########################################################################
# Class name: Room
# Function: This class handles all routines associated with room heating controls
########################################################################
class Room():
    def __init__(
        self,
        name,
        floor,
        week_day,
        weekend,
        office,
        empty,
        fixed,
        user1,
        user2,
        default_mode,
        days,
        radiators,
        heaters,
        room_temp_sensor,
        room_thermostat = "",
        demand_factor = 1,
        rad_offset = 0
    ):
        self.name = name
        self.floor = floor
        self.week_day = week_day
        self.weekend = weekend
        self.office = office
        self.empty = empty
        self.fixed = fixed
        self.user1 = user1
        self.user2 = user2
        self.default_mode = default_mode
        self.radiators = radiators
        self.heaters = heaters
        self.room_temp_sensor = room_temp_sensor
        self.room_thermostat = room_thermostat
        self.demand_factor = demand_factor
        self.rad_offset = rad_offset

# Create a list that will hold all of your room instances
room_instances = list()

#### Room Definitions
# We specify week_day as a variable since we use it multiple times when creating the
# lounge instance
week_day = ['00:00', 17, '08:00', 19, '10:00', 20, '16:00', 21, '22:30', 21, '23:00', 17]
# The next two are just really long lists, so to keep the lines to PEP standards we
# define them beforehand and put one on each line.
# Note for the days list: I'm not sure that this is really what you want, but because
# I don't know how you're using the days list maybe it is. It looks a little odd though
# having the WeekDay item used after every day...
days = [
    'Mon',
    'WeekDay',
    'Tue',
    'WeekDay',
    'Wed',
    'WeekDay',
    'Thu',
    'WeekDay',
    'Fri',
    'WeekEnd',
    'Sat',
    'WeekEnd',
    'Sun',
    'WeekEnd'
]
radiators = [
    'RadStat Lounge Main',
    'RadStat Lounge Left Window',
    'RadStat Lounge Right Window'
]
# While not really necessary, when you have an object instantiation (__init__) that has
# a bunch of parameters, it's usually best to put them one per line and use the param
# name so that it's clear to anyone trying to read the code.
lounge = Room(
    name="Lounge",
    floor="0",
    week_day=week_day,
    weekend=['00:00', 17, '08:00', 19, '10:00', 20, '16:00', 21, '22:30', 21, '23:59', 17],
    office=week_day,
    empty=['00:00', 17, '08:00', 19, '10:00', 19, '16:00', 19, '22:30', 19, '23:00', 17],
    fixed=['00:00', 20],
    user1=['00:00', 20],
    user2=['00:00', 20],
    default_mode='Normal',
    days=days,
    radiators=radiators,
    heaters=[],
    room_temp_sensor="Lounge - T",
    room_thermostat="RoomStat Lounge",
    demand_factor=2,
    rad_offset=2.5
)
room_instances.append(lounge)

# I won't do the rest of them, but you get the picture.
# Dining Room
name="DiningRoom"
floor ="0"
WeekDay = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30', 21,'23:00',17]
WeekEnd = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30', 21,'23:59',17]
Office = WeekDay
Empty = ['00:00',17,'06:30',19,'10:00',19,'16:00',19,'22:30', 19,'23:00',17]
Fixed = ['00:00',20]
User1 = ['00:00',20]
User2 = ['00:00',20]
defaultMode = 'Normal'
days = ['Mon','WeekDay','Tue','WeekDay','Wed','WeekDay','Thu','WeekDay','Fri','WeekEnd','Sat','WeekEnd','Sun','WeekEnd']
radiators = ['RadStat Dining Room']
heaters = []
dining_room = Room(name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,room_temp_sensor="Dining Room - T",room_thermostat="",demand_factor=0.5,rad_offset=1)
room_instances.append(dining_room)

# Kitchen
name="Kitchen"
floor ="0"
WeekDay = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30',21,'23:00',17]
WeekEnd = ['00:00',17,'06:30',21,'10:00',20,'16:00',21,'22:30',21,'23:30',17]
Office = WeekDay
Empty = ['00:00',17,'06:30',19,'10:00',19,'16:00',19,'22:30',19,'23:00',17]
Fixed = ['00:00',20]
User1 = ['00:00',20]
User2 = ['00:00',20]
defaultMode = 'Normal'
days = ['Mon','WeekDay','Tue','WeekDay','Wed','WeekDay','Thu','WeekDay','Fri','WeekEnd','Sat','WeekEnd','Sun','WeekEnd']
radiators = ['RadStat Kitchen','RadStat Utility Room']
heaters = []
kitchen = Room(name,floor,WeekDay,WeekEnd,Office,Empty,Fixed,User1,User2,defaultMode,days,radiators,heaters,room_temp_sensor="Kitchen - T",room_thermostat="",demand_factor=1,rad_offset=-1)
room_instances.append(kitchen)


########################################################################
# MAIN ROUTINE
########################################################################
# Cycle through them all
for room in room_instances:
   indigo.server.log(f"Room -  {room.name}", type="My Script")

# Find a specific Room by name
matches = [room for room in room_instances if room.name == "Kitchen"]
# matches will be a list containing the kitchen instance


[MODERATOR NOTE]: moved topic to the Python Scripting forum since that's the more appropriate place for it.

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Posted on
Wed Jul 27, 2022 11:48 am
RedYuriPrime offline
Posts: 58
Joined: Oct 14, 2014
Location: Oxford

Re: Migrating to Moterey & python 3.1 - iterating class inst

Many thanks for the input Jay.

Got test code running in ST3 demonstrating it works. Took me a few minutes to realise that in order to use form :

matches.name

I needed to slice from the list:

Code: Select all
matches = [room for room in room_instances if room.name == "DiningRoom"][0]


Now just need to transfer into my live code ...

One thing I have found with the move to python 3.1 is that most python examples on the internet seem to be in python 2.x (where it matters), and that this is rarely made clear. Hence using metaclasses to iterate through class instances was easy to find and set up in 2.7 , and made quite readable code.
For me, code efficiency is less of a concern - the new Mac Mini is idle 95% of the time when running both Indigo and the remote access software - I guess the position may be different if you are also running other security software as well.

Z-wave system with 80+ devices, Indigo7 on a dedicated MacMini
Controlling lighting, heating, hotwater, security; but still a lot to learn ... :shock:

Posted on
Wed Jul 27, 2022 5:57 pm
jay (support) offline
Site Admin
User avatar
Posts: 18221
Joined: Mar 19, 2008
Location: Austin, Texas

Re: Migrating to Moterey & python 3.1 - iterating class inst

RedYuriPrime wrote:
I needed to slice from the list


Yes, see the very last line of my code... :wink:

Jay (Indigo Support)
Twitter | Facebook | LinkedIn

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 30 guests