Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Dictionary help
#1
I am working on a simple telnet program that interacts with a Lutron lighting system. The Lutron system allows me to enter a monitoring mode where it will display the status of a device/area. When a device activates, it changes state and triggers an area scene.

The following is output to the telnet command line:
Output:
~DEVICE,179,1,3 ~AREA,161,6,3
Currently I have 2 dictionaries
device_ids = {'179,1': 'Dressing 3', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}
area_status ={'161': 'Dressing 3', '11': 'Dressing 2', '170': 'Dressing 1'}
Using regex I extract the information I need to print the location name. I would like to incorporate some error checking into my code to ensure that when a device activates, the scene is triggered too. Something like - If Device 179,1 is active check to see if Area 161 is active. The dictionaries are quite large so manually writing 'if' statements seems like the wrong way to go about things.

If I were to store the information like this:

locations = {'Dressing Room 3': {'Device ID': '279,1', 'Device Status': '1',
'Area ID': '161', 'Area Status': '1' }}

1. Is there a way to print the Key name (Dressing Room 3)from the value? e.g when the system reports '~DEVICE,179,1,3,' how do i make it print' Dressing Room 3

2. Could this be done a better way with a database?

Thank you.

Here's the full code:
def lms():
    occ_pattern = re.compile("^[DEVIC]+[,]([(0-9)]+[,][0-9]+)[,]([0-9]+)")
    area_pattern = re.compile("^[ARE]+[,]([(0-9)]+)[,][0-9]+[,]([0-9]+)")

    tn.write(occupancy_mon_enable.encode('ascii') + b'\r\n')
    print('Occupancy Monitoring Enabled')

    tn.read_until(b'QNET> ')
    tn.write(scene_mon_enable.encode('ascii') + b'\r\n\n')
    print('Scene Monitoring Enabled')

    while True:
        tn.read_until(b'~')
        output = tn.read_very_eager().decode('utf-8')

        try:
            status = ' '
            occ_id = occ_pattern.search(output).group(1)
            occ_status = occ_pattern.search(output).group(2)

            if occ_status == '3':
                status = 'Active'
            elif occ_status == '4':
                status = 'Inactive'

            print('DEVICE,' + occ_id + "," + occ_status + ": " + device_ids[occ_id] + " - " + status)

        except AttributeError:
            pass

        try:
            scene = ' '
            area_id = area_pattern.search(output).group(1)
            area_scene = area_pattern.search(output).group(2)

            if area_scene == '1':
                scene = 'Midnight'
            elif area_scene == '2':
                scene = 'Sunrise'
            elif area_scene == '3':
                scene = 'Day'
            elif area_scene == '4':
                scene = 'Sunset'
            elif area_scene == '5':
                scene = 'Evening'
            elif area_scene == '0':
                scene = 'Off'
            else:
                pass

            print("AREA," + area_id + "," + area_scene + ": " + area_status[area_id] + " - " + scene)

        except AttributeError:
            pass
Yoriz write Dec-06-2022, 02:15 PM:
Please post all code, output and errors (in their entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Reply
#2
iuno Wrote:1. Is there a way to print the Key name (Dressing Room 3)from the value? e.g when the system reports '~DEVICE,179,1,3,' how do i make it print' Dressing Room 3

device_ids = {'179,1': 'Dressing 3', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}
area_status ={'161': 'Dressing 3', '11': 'Dressing 2', '170': 'Dressing 1'}

output = '~DEVICE,179,1,3,'.split(',')
key = f"{output[1]},{output[2]}"

value = device_ids[key]
print(f"device_name: {value}", )


for key in area_status.keys():
    if area_status[key] == value:
        print(f"area_status: {key}")
iuno Wrote:2. Could this be done a better way with a database?

A database really isn't needed here.

if would be easier to rearrange area_status dictionary as so:
area_status ={'Dressing 3': '161', 'Dressing 2': '11', 'Dressing 1': '170'}

# then: print(f"status: {area_status[value]}") would get the status.

new code would be:
device_ids = {'179,1': 'Dressing 3', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}
area_status ={'Dressing 3': '161', 'Dressing 2': '11', 'Dressing 1': '170'}

output = '~DEVICE,179,1,3,'.split(',')
key = f"{output[1]},{output[2]}"

value = device_ids[key]
print(f"device_name: {value}", )
print(f"status: {area_status[value]}")
Output:
device_name: Dressing 3 status: 161
Reply
#3
Thanks for your help.

Using your method I can now achieve what I wanted.

I will need 4 dictionaries that will look like this (I've renamed them to help describe their purpose)

device_id_number = {'179,1': 'Dressing 3', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}
area_id_number = {'Dressing 3': '161', 'Dressing 2': '11', 'Dressing 1': '170'}
device_activity_status = {'179,1': 0, '179,2': 0, '179,3': 1}
area_activity_status = {'Dressing 3': 0, 'Dressing 2': 0, 'Dressing 1': 1}
Could this be bettered in any way?
Reply
#4
I'm not sure if I am following this completely, but it looks like classes could be useful. Classes work better for collecting related information than having a bunch of interrelated dictionaries. Classes also let you associate code with the data. Below I have a Device class and an Area class. When you set the status of an Area it sets the status of all the devices in the area. You could write other methods that check if all devices in an area have the same status. Get an area from a device and tell all the other devices in the area to be the same, etc...
class Area():
    instances = {}

    @classmethod
    def get(cls, name):
        area = cls.instances.get(name, None)
        if area is None:
            area = Area(name)
        return area

    def __init__(self, name, status=0):
        self.name = name
        self.devices = {}
        self.status = status
        self.instances[name] = self

    def add_device(self, name, device):
        self.devices[name] = device

    def set_status(self, value):
        self.status = value
        for device in self.devices.values():
            device.set_status(value)

    def __str__(self):
        return f'<Area: {self.name}, status:{self.status}>'


class Device():
    instances = {}
    def __init__(self, name, status, area):
        self.name = name
        self.status = 0
        self.area = Area.get(area)
        self.area.add_device(name, self)
        self.instances[self.name] = self

    def set_status(self, value):
        self.status = value

    def __str__(self):
        return f'<Device: {self.name}, area:{self.area.name}, status:{self.status}>'

device_ids = {'179,1': 'Dressing 2', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}

for device, area in device_ids.items():
    Device(device, 0, area)

print("\nDevices")
for device in Device.instances.values():
    print(device)

Area.get('Dressing 2').set_status(1)
print("\nDevices")
for device in Device.instances.values():
    print(device)
Output:
Devices <Device: 179,1, area:Dressing 2, status:0> <Device: 179,2, area:Dressing 2, status:0> <Device: 179,3, area:Dressing 1, status:0> Devices <Device: 179,1, area:Dressing 2, status:1> <Device: 179,2, area:Dressing 2, status:1> <Device: 179,3, area:Dressing 1, status:0>
Reply
#5
edit. duplicate.
Reply
#6
(Dec-06-2022, 08:57 PM)deanhystad Wrote: I'm not sure if I am following this completely, but it looks like classes could be useful. Classes work better for collecting related information than having a bunch of interrelated dictionaries. Classes also let you associate code with the data. Below I have a Device class and an Area class. When you set the status of an Area it sets the status of all the devices in the area. You could write other methods that check if all devices in an area have the same status. Get an area from a device and tell all the other devices in the area to be the same, etc...
class Area():
    instances = {}

    @classmethod
    def get(cls, name):
        area = cls.instances.get(name, None)
        if area is None:
            area = Area(name)
        return area

    def __init__(self, name, status=0):
        self.name = name
        self.devices = {}
        self.status = status
        self.instances[name] = self

    def add_device(self, name, device):
        self.devices[name] = device

    def set_status(self, value):
        self.status = value
        for device in self.devices.values():
            device.set_status(value)

    def __str__(self):
        return f'<Area: {self.name}, status:{self.status}>'


class Device():
    instances = {}
    def __init__(self, name, status, area):
        self.name = name
        self.status = 0
        self.area = Area.get(area)
        self.area.add_device(name, self)
        self.instances[self.name] = self

    def set_status(self, value):
        self.status = value

    def __str__(self):
        return f'<Device: {self.name}, area:{self.area.name}, status:{self.status}>'

device_ids = {'179,1': 'Dressing 2', '179,2': 'Dressing 2', '179,3': 'Dressing 1'}

for device, area in device_ids.items():
    Device(device, 0, area)

print("\nDevices")
for device in Device.instances.values():
    print(device)

Area.get('Dressing 2').set_status(1)
print("\nDevices")
for device in Device.instances.values():
    print(device)
Output:
Devices <Device: 179,1, area:Dressing 2, status:0> <Device: 179,2, area:Dressing 2, status:0> <Device: 179,3, area:Dressing 1, status:0> Devices <Device: 179,1, area:Dressing 2, status:1> <Device: 179,2, area:Dressing 2, status:1> <Device: 179,3, area:Dressing 1, status:0>

Thanks, I think you may be right. I will give this a shot.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020