Seite 1 von 1
Erstellen eines Dict
Verfasst: Sonntag 19. Januar 2020, 18:06
von sisamiwe
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
Re: Erstellen eines Dict
Verfasst: Sonntag 19. Januar 2020, 18:43
von Sirius3
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.
Re: Erstellen eines Dict
Verfasst: Montag 20. Januar 2020, 21:19
von sisamiwe
@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.
Re: Erstellen eines Dict
Verfasst: Montag 20. Januar 2020, 22:01
von sisamiwe
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?
Re: Erstellen eines Dict
Verfasst: Montag 20. Januar 2020, 22:11
von Sirius3
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.
Re: Erstellen eines Dict
Verfasst: Dienstag 21. Januar 2020, 06:48
von sparrow
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'}
Re: Erstellen eines Dict
Verfasst: Dienstag 21. Januar 2020, 15:14
von sisamiwe
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.
Re: Erstellen eines Dict
Verfasst: Dienstag 21. Januar 2020, 15:27
von sparrow
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.
Re: Erstellen eines Dict
Verfasst: Dienstag 21. Januar 2020, 15:32
von sisamiwe
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?
Re: Erstellen eines Dict
Verfasst: Dienstag 21. Januar 2020, 15:37
von __blackjack__
@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``.
Re: Erstellen eines Dict
Verfasst: Mittwoch 22. Januar 2020, 12:44
von __blackjack__
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
Re: Erstellen eines Dict
Verfasst: Mittwoch 22. Januar 2020, 13:25
von sisamiwe
Danke.
Ich probiere das heute mal aus.