Erstellen eines Dict

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

Hallo,

ich beschäftige mich gerade mit einer Neuentwicklung des Viessmann Plugins, mit Lese- und Schreibfunktion und direktem Zugriff über die serielle Schnittstelle.
Viele Funktionen sind bereits implementiert und getestet. Aktuell stehen die Timer (Schaltzeiten) an und dabei bräuchte ich Hilfe bei der Umsetzung in Python.

Konkret:
Die Abfrage der Schaltzeiten liefert ein Bytearray in dem die möglichen 4 An- und 4 Abschaltzeiten enthalten sind. diese möchte ich in ein dict übertragen.
Das Dict soll so aussehen: {1{'AN':'Zeit1'; 'AUS':'Zeit2'};2{'AN':'Zeit3'; 'AUS':'Zeit4'};3{'AN':'Zeit5'; 'AUS':'Zeit6'};4{'AN':'Zeit7'; 'AUS':'Zeit8'}}

Das bytearray sieht bspw so aus: b'20248389ffffffff'
Immer 2 Byte stehen für eine Schaltzeit.

Mein PythonCode zur Ermittelung der Zeit sieht so aus:

Code: Alles auswählen

timerdict = {}
index = 0
while (len(rawdatabytes) > 0):
    # Ersten 2Byte zwischenspeichern
    leftbytes = rawdatabytes[:2]
    # Wert der Bytes ermitteln
    value = int(leftbytes, 16)
    if value != 255:
      hour = int(value>>3)
      minute = int((value-(hour<<3))*10)
    else:
      hour = 0
      minute = 0
    zeit2 = str(hour).zfill(2) + ':' + str(minute).zfill(2)
    rawdatabytes = rawdatabytes[2:]
    
Wie bekomme ich die Schaltezeit am besten in das Dict nach obiger Form?
Danke Euch
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Ist es korrekt, dass ein nicht gültiger Zeitwert zu 00:00 wird?
Was ist mit anderen nicht gültigen Zeiten? Wenn also die Minuten >=60 sind?
str und zfill sollte man nicht benutzen, weil es statt dessen Stringformatierung gibt.
Am besten ist es, ein Problem in einzelne Teilprobleme aufzuteilen.
1. Das Umwandeln in Zeiten:

Code: Alles auswählen

def iter_times(rawdata):
    while rawdata:
        hours, minutes = divmod(int(rawdata[:2], 16), 8)
        if minutes >= 6:
            yield f"00:00" # keine gültiger Zeit-Wert
        else:
            yield f"{hours:02d}:{minutes*10:02d}"
        rawdata = rawdata[2:]
und dem Füllen einer Liste mit jeweils An- und Auszeiten:

Code: Alles auswählen

times = iter_times(rawdata)
times = [{'an': on_time, 'aus': off_time}
    for on_time, off_time in zip(times, times)]
Das äußere Wörterbuch scheint mir eher wie eine Liste.
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

@Sirius3

Vielen Dank für Deine Hilfe.
Das hat sofort geklappt.

Ist es korrekt, dass ein nicht gültiger Zeitwert zu 00:00 wird?
--> Ich habe das auf --:-- geändert. Ist besser.
Was ist mit anderen nicht gültigen Zeiten? Wenn also die Minuten >=60 sind?
--> Die Heizung gibt nur richtige Werte aus. Dein Vorschlag ist aber Super. Ist der Timer nicht benutzt, wird ff also 255 rückgemeldet.
Das äußere Wörterbuch scheint mir eher wie eine Liste.
--> Ich würde gern vor den jeweiligen An/Aus Paar noch die Eintragsnummer haben wollen Also so
[1 {'An': '04:00', 'Aus': '04:40'}, 2 {'An': '16:30', 'Aus': '17:10'}, 3 {'An': '--:--', 'Aus': '--:--'}, 4 {'An': '--:--', 'Aus': '--:--'}] so das ich dann die Zeiten auch direkt abfragen kann.
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

Ich bräuchte hier auch noch einmal Hilfe.
Ich habe ein bytearray, in dem das Datum und Uhrzeit versteckt ist.

Meine Lösung:

Code: Alles auswählen

from datetime import datetime
rawdatabytes = b'1970122304000000'
print(rawdatabytes)
year = int(rawdatabytes[:4])
month = int(rawdatabytes[4:6])
day = int(rawdatabytes[6:8])
weekday = int(rawdatabytes[8:10])
hour = int(rawdatabytes[10:12])
minute = int(rawdatabytes[12:14])
second = int(rawdatabytes[14:16])
datetime = datetime(year, month, day, hour, minute, second)
print(datetime)
Das geht doch bestimmt auch eleganter?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

Code: Alles auswählen

datetime.strptime(rawdatabytes.decode(), '%Y%m%d%W%H%M%S')
`datetime` ist der Name einer Klasse und sollte nicht auch der Name einer Variable sein.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

sisamiwe hat geschrieben: Montag 20. Januar 2020, 21:19Das äußere Wörterbuch scheint mir eher wie eine Liste.
--> Ich würde gern vor den jeweiligen An/Aus Paar noch die Eintragsnummer haben wollen Also so
[1 {'An': '04:00', 'Aus': '04:40'}, 2 {'An': '16:30', 'Aus': '17:10'}, 3 {'An': '--:--', 'Aus': '--:--'}, 4 {'An': '--:--', 'Aus': '--:--'}] so das ich dann die Zeiten auch direkt abfragen kann.
Wenn du ein Wörterbuch benutzen möchtest, um die Einträge durchzunummerieren, willst du eigentlich eine Liste. Da hat Sirius3 schon recht.
Du weißt, dass man die Elemente einer Liste per Index ansprechen kann?

Code: Alles auswählen

>>> a = [
	{'An': '04:00', 'Aus': '04:40'},
	{'An': '16:30', 'Aus': '17:10'},
	{'An': '--:--', 'Aus': '--:--'},
	{'An': '--:--', 'Aus': '--:--'}
	]
>>> a[0]
{'An': '04:00', 'Aus': '04:40'}
>>> a[1]
{'An': '16:30', 'Aus': '17:10'}
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

sparrow hat geschrieben: Dienstag 21. Januar 2020, 06:48
sisamiwe hat geschrieben: Montag 20. Januar 2020, 21:19Das äußere Wörterbuch scheint mir eher wie eine Liste.
--> Ich würde gern vor den jeweiligen An/Aus Paar noch die Eintragsnummer haben wollen Also so
[1 {'An': '04:00', 'Aus': '04:40'}, 2 {'An': '16:30', 'Aus': '17:10'}, 3 {'An': '--:--', 'Aus': '--:--'}, 4 {'An': '--:--', 'Aus': '--:--'}] so das ich dann die Zeiten auch direkt abfragen kann.
Wenn du ein Wörterbuch benutzen möchtest, um die Einträge durchzunummerieren, willst du eigentlich eine Liste. Da hat Sirius3 schon recht.
Du weißt, dass man die Elemente einer Liste per Index ansprechen kann?
Danke, dass weiß ich.

Wahrscheinlich habe ich zu wenige Information gegeben.
Neben der Lesefunktion (Einlesen des Bytestring, Decodieren der Schaltzeiten, formatieren als Liste oder Wörterbuch) möchte ich auch die Schreibfunktion realisieren. Dort muss natürlich genau anderesherum gearbeitet werden, d.h. Auslesen der Liste oder des Wörterbuches, Codieren der Schaltzeiten in Bytes und ertellern des Bytestring zum Senden.
Konkret bedeutet das am Beispiel oben: Auslesen der 04:00 und umwandeln in 2 Byte; auslesen der 04:40 , umwandeln in 2 Byte und anhängen an die ersten beiden Bytes etc bis die 16 bytes vollständig sind. Nicht besetzte Zeiten werden zu 255 bzw. ff.

Ich denke, dass die Aufgabe des Auslesen mit einem Dict einfacher wäre, lerne aber gern dazu.
Danke Euch.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Aber warum sollte das denn einfacher sein?
Du nummerierst die Einträge doch eh durch. Damit fummelst du dir ja nur umständlich selbst eine Liste zusammen.
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

sparrow hat geschrieben: Dienstag 21. Januar 2020, 15:27 Aber warum sollte das denn einfacher sein?
Du nummerierst die Einträge doch eh durch. Damit fummelst du dir ja nur umständlich selbst eine Liste zusammen.
Konkret: Wie kann ich aus der bestehenden Liste die Daten also nur die Zeiten wieder auslesen?
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@sisamiwe: Ich würde da auch noch mal grundsätzlich Wörterbücher in Frage stellen. Ein Wörterbuch das immer die gleichen Schlüssel hat, ist doch eigentlich ein Datenobjekt und kein Wörterbuch. Und Zeiten als Zeichenketten mit Stunden/Minuten sieht auch eher nach einem Format zum serialisieren als JSON oder so aus, innerhalb eines Programms würde ich das als `datetime.time` modellieren.

Zur letzten Frage: Du iterierst über die Elemente der Liste. Also ``for timespan in timespans:``. Das ist doch deutlich einfacher als ``for i in range(1, len(timespans) + 1): timespan = timespans``.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mal ein Ansatz mit einer eigenen Klasse für die Zeitpaare:

Code: Alles auswählen

#!/usr/bin/env python3
from datetime import time as Time
from pprint import pprint

from attr import attrib, attrs
from more_itertools import grouper


def decode_time(byte_value):
    if byte_value == 0xFF:
        return None

    hour, decaminute = divmod(byte_value, 8)
    return Time(hour, decaminute * 10)


def encode_time(time):
    if time is None:
        return 0xFF

    decaminute, remainder = divmod(time.minute, 10)
    if remainder:
        raise ValueError(f"minute ({time.minute}) not multiple of 10")
    return time.hour << 3 | decaminute


@attrs(frozen=True)
class Timespan:
    start = attrib()
    end = attrib()

    def as_bytes(self):
        return bytes([encode_time(self.start), encode_time(self.end)])

    @classmethod
    def from_bytes(cls, values):
        start_value, end_value = values
        return cls(decode_time(start_value), decode_time(end_value))


def decode_timespans(byte_values):
    return map(Timespan.from_bytes, grouper(2, byte_values))


def encode_timespans(timespans):
    return b"".join(timespan.as_bytes() for timespan in timespans)


def main():
    text = "20248389ffffffff"
    timespans = list(decode_timespans(bytes.fromhex(text)))
    pprint(timespans)
    encoded_timespans = encode_timespans(timespans)
    print(encoded_timespans.hex())


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

[Timespan(start=datetime.time(4, 0), end=datetime.time(4, 40)),
 Timespan(start=datetime.time(16, 30), end=datetime.time(17, 10)),
 Timespan(start=None, end=None),
 Timespan(start=None, end=None)]
20248389ffffffff
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
sisamiwe
User
Beiträge: 17
Registriert: Freitag 31. März 2017, 20:15

Danke.
Ich probiere das heute mal aus.
Antworten