From f5c5ac8d8c1e4406c4731f9d02755a49fa205ce9 Mon Sep 17 00:00:00 2001 From: "S. Seegel" Date: Sat, 5 Dec 2020 23:37:35 +0100 Subject: [PATCH] add pilota casa protokoll --- README.md | 26 +--- apps/fs20.py | 178 --------------------------- apps/rcprotocols.py | 73 +++++++++-- apps/{rfmnodered.py => rcpulsegw.py} | 1 + fs20tx.py | 83 ------------- 5 files changed, 69 insertions(+), 292 deletions(-) delete mode 100644 apps/fs20.py rename apps/{rfmnodered.py => rcpulsegw.py} (97%) mode change 100755 => 100644 delete mode 100755 fs20tx.py diff --git a/README.md b/README.md index 9afe0b5..2cdc157 100644 --- a/README.md +++ b/README.md @@ -35,30 +35,12 @@ see https://power-switch.eu/ ## emoncms.py receive lacrosse-sensors with the RaspyRFM and post them to the open energy monitor, see https://openenergymonitor.org/ -## fs20tx.py -controlling FS20 RX sockets -```sh -sudo ./fs20tx
-``` - -## intertechno.py -controlling remote control sockets -```sh -./rcpulse.py -p ittristate -o -g -u -s <0|1> #control old intertechno sockets -./rcpulse.py -p ittristate -c <12 symbols tristate code> #control old intertechno sockets -rcpulse <26 bit address 0|1> <1 goup bit 0|1> <4 bit unit 0|1> on|off #control intertechno self learning -rcpulse <32 bit code 0|1> #control intertechno and compatible (HAMA, REV) -rcpulse <5 DIP 0|1> on|off #control Brennenstuhl RC1000 -usage example: -./rcpulse.py -p ittristate -o A -g 1 -u 1 -s 1 -./rcpulse.py -p ittristate -c 0000FFFF0FFF -./rcpulse 11110000111100001111000010 0 1110 on -./rcpulse 11110000111100001111000010010000 -``` - -## Receive 433 MHz RC remote controls +## Receive and send 433 MHz RC remote controls ```sh ./rcpulse.py +./rcpulse.py -p intertechno -i 47612 -u 1 -a on +./rcpulse.py -p logilight -i 76123 -u 1 -a on +./rcpulse.py -p pilota -i 1234 -g 1 -u 1 -a on ``` ## Receive 868 MHz ELV FS20 RC remote controls diff --git a/apps/fs20.py b/apps/fs20.py deleted file mode 100644 index cea4453..0000000 --- a/apps/fs20.py +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env python2.7 - -import re - -PROTOCOL = "FS20" - -def __parse_byte(bits): - i = int(bits, 2) - cnt = 0 - tmp = i - while tmp > 0: - cnt += 1 - tmp &= tmp - 1 - if (cnt & 1) == 0: - return i>>1 - else: - return -1 - -def Decode(pulses): - if len(pulses) != 118: - print(len(pulses), pulses) - return - - sym = "" - s = 0 - for p in pulses: - if (p >= 300) and (p <= 500): - sym += 's' - s += p - elif (p > 500) and (p <= 750): - sym += 'l' - s += p - else: - sym += '?' - - bits = "" - for i in range(59): - if sym[:2] == 'ss': - bits += "0" - elif sym[:2] == "ll": - bits += "1" - else: - bits +="?" - sym = sym[2:] - - print(bits) - #check for sync sequence - if bits[:13] != "0000000000001": - return - bits = bits[13:] #strip off sync sequence - - by = [] - while len(bits) >= 9: - by.append(__parse_byte(bits[:9])) - bits = bits[9:] - - #check checksum - cs = 6 - for i in range(len(by) - 1): - cs += by[i] - cs = by[-1:][0] - (cs & 0xff) - if (cs > 2) or (cs < 0): - return - - ret = { - "protocol": PROTOCOL, - "housecode": "{:04X}".format((by[0] << 8) | by[1]), - "address": "{:02X}".format(by[2]), - "command": "{:02X}".format(by[3]) if len(by) == 5 else "{:04X}".format(by[3] << 8 | by[4]) - } - print(ret) - - return - return ("fs20", bits, int(round(100 / (12.0 * 8 + 1)))) - -def Encode(args): - da = [] - bp = [0] - bv = [0] - def add_pulse(val, length): - for i in range(length): - bp[0] += 1 - bv[0] <<= 1 - bv[0] |= val - if bp[0] == 8: - da.append(bv[0]) - bv[0] = 0 - bp[0] = 0 - - def add_bit(b): - if b == 0: - add_pulse(1, 2) - add_pulse(0, 2) - else: - add_pulse(1, 3) - add_pulse(0, 3) - - def add_byte(by): - par = 0 - mask = 0x80 - while (mask): - add_bit(by & mask) - par >>= 1 - par ^= (by & mask) - mask >>= 1 - add_bit(par) - - #add sync pattern - for i in range(12): - add_bit(0) - add_bit(1) - - hc = int(args[0], 16) - adr = int(args[1], 16) - cmd = int(args[2], 16) - #calculate checksum - q = (6 + (hc >> 8) + (hc & 0xFF) + adr + (cmd >> 8) + (cmd & 0xFF)) & 0xff - #add housecode - add_byte(hc >> 8) - add_byte(hc & 0xFF) - #add address - add_byte(adr) - #add command - add_byte(cmd) - #add checksum - add_byte(q) - #add EOT - add_bit(0) - add_pulse(0, 33) - if bp[0] > 0: - add_pulse(0, 8-bp[0]) - return (da, 3, 200) - - code = ' '.join(args) - if re.match("^[01Ff]{12}$", code): - data = [] - for c in code: - if c == '0': - data.append(0x88) - elif c == '1': - data.append(0xEE) - elif c in ['F', 'f']: - data.append(0x8E) - data += [0x80, 0x00, 0x00, 0x00] #sync - - return (data, 5, 360) - - elif re.match('^[A-P] [1-4] [1-4] (on|off)$', code): - g = re.match('^([A-P]) ([1-4]) ([1-4]) (on|off)$', code).groups() - tristate = "" - tristate += encodeBits(ord(g[0]) - ord('A'), 4) #housecode - tristate += encodeBits(ord(g[2]) - 1, 2) #channel - tristate += encodeBits(ord(g[1]) - 1, 2) #group - tristate += "0F" - tristate += 'FF' if g[3] == 'on' else 'F0' - return Encode([tristate]) - - elif re.match('^([01]{5}) ([1-4]) (on|off)$', code): #Brennenstuhl - g = re.match('^([01]{5}) ([1-4]) (on|off)$', code).groups() - tristate = "" - for c in g[0]: - tristate += '0' if c == '1' else 'F' - for i in range(4): - tristate += '0' if int(g[1]) - 1 == i else 'F' - tristate += 'F' - tristate += '0F' if g[2] == 'on' else 'F0' - return Encode([tristate]) - - elif re.match('^[1-4] [1-4] (on|off)$', code): - g = re.match('^([1-4]) ([1-4]) (on|off)$', code).groups() - tristate = "" - for i in range(4): - tristate += "0" if int(g[0]) - 1 == i else "F" - for i in range(4): - tristate += "0" if int(g[1]) - 1 == i else "F" - tristate += "FFF" - tristate += 'F' if g[2] == 'on' else '0' - return Encode([tristate]) \ No newline at end of file diff --git a/apps/rcprotocols.py b/apps/rcprotocols.py index b98fd61..f580f60 100644 --- a/apps/rcprotocols.py +++ b/apps/rcprotocols.py @@ -4,6 +4,8 @@ from raspyrfm import * import threading import time +PARAM_ID = ('i', 'id') + class RcProtocol: def __init__(self): self.__numbits = 0 @@ -225,7 +227,7 @@ class Intertechno(PPM1): self._name = "intertechno" self._timebase = 275 self.params = [ - ('i', 'id'), + PARAM_ID, ('u', 'unit'), ('a', 'command'), ] @@ -272,7 +274,7 @@ class PWM1(RcProtocol): PWM1: Pulse Width Modulation Wide pulse -> 1, small pulse -> 0 Frame: header, payload, footer - Used by Intertechno self learning, Hama, ... + Used by Emylo, Logilight, ... ''' def __init__(self): self._timebase = 300 @@ -290,7 +292,7 @@ class Logilight(PWM1): PWM1.__init__(self) self._name = "logilight" self.params = [ - ('i', 'id'), + PARAM_ID, ('u', 'unit'), ('a', 'command'), ] @@ -336,7 +338,7 @@ class Emylo(PWM1): PWM1.__init__(self) self._name = "emylo" self.params = [ - ('i', 'id'), + PARAM_ID, ('k', 'key'), ] self.__keys = {'A': '0001', 'B': '0010', 'C': '0100', 'D': '1000'} @@ -379,7 +381,7 @@ class FS20(RcProtocol): self._header = [2, 2] * 12 + [3, 3] self._footer = [1, 100] self.params = [ - ('i', 'id'), + PARAM_ID, ('u', 'unit'), ('a', 'command') ] @@ -437,7 +439,7 @@ class Voltcraft(RcProtocol): self._header = [1] self._footer = [132] self.params = [ - ('i', 'id'), + PARAM_ID, ('u', 'unit'), ('a', 'command') ] @@ -483,7 +485,7 @@ class PWM2(RcProtocol): def __init__(self): self._name = "pwm2" self._timebase = 600 - self._repetitions = 6 + self._repetitions = 10 self._pattern = "[01]{32}" self._symbols = { '1': [1, 2], @@ -495,6 +497,10 @@ class PWM2(RcProtocol): ] RcProtocol.__init__(self) + def encode(self, params, timebase=None, repetitions=None): + symbols = params["code"] + return self._build_frame(symbols, timebase, repetitions) + def decode(self, pulsetrain): symbols, tb, rep = self._decode_symbols(pulsetrain[:-2]) @@ -511,9 +517,57 @@ class PWM2(RcProtocol): "timebase": tb, }, rep +class PilotaCasa(PWM2): + __codes = { + '110001': (1, 1, 'on'), '111110': (1, 1, 'off'), + '011001': (1, 2, 'on'), '010001': (1, 2, 'off'), + '101001': (1, 3, 'on'), '100001': (1, 3, 'off'), + '111010': (2, 1, 'on'), '110010': (2, 1, 'off'), + '010110': (2, 2, 'on'), '011010': (2, 2, 'off'), + '100110': (2, 3, 'on'), '101010': (2, 3, 'off'), + '110111': (3, 1, 'on'), '111011': (3, 1, 'off'), + '011111': (3, 2, 'on'), '010111': (3, 2, 'off'), + '101111': (3, 3, 'on'), '100111': (3, 3, 'off'), + '111101': (4, 1, 'on'), '110101': (4, 1, 'off'), + '010011': (4, 2, 'on'), '011101': (4, 2, 'off'), + '100011': (4, 3, 'on'), '101101': (4, 3, 'off'), + } + + def __init__(self): + PWM2.__init__(self) + self._name = "pilota" + self.params = [ + PARAM_ID, + ('g', 'group'), + ('u', 'unit'), + ('a', 'command') + ] + def encode(self, params, timebase=None, repetitions=None): - symbols = params["code"] + symbols = '01' + u = None + for k, v in self.__codes.items(): + if v[0] == int(params["group"]) and v[1] == int(params["unit"]) and v[2] == params["command"]: + u = k + break + symbols += u + symbols += "{:016b}".format(int(params["id"]))[::-1] + symbols += "11111111" + print(symbols) return self._build_frame(symbols, timebase, repetitions) + + def decode(self, pulsetrain): + symbols, tb, rep = self._decode_symbols(pulsetrain[:-2]) + + if symbols and (symbols[2:8] in self.__codes): + c = self.__codes[symbols[2:8]] + params = { + "id": int(symbols[8:24][::-1], 2), + "group": c[0], + "unit": c[1], + "command": c[2], + } + return self._name, params, tb, rep class PCPIR(RcProtocol): #pilota casa PIR sensor def __init__(self): @@ -551,12 +605,13 @@ protocols = [ Hama(), Logilight(), Emylo(), - PWM2(), + #PWM2(), Voltcraft(), #PDM32(), #PCPIR(), #PDM1(), FS20(), + PilotaCasa() ] def get_protocol(name): diff --git a/apps/rfmnodered.py b/apps/rcpulsegw.py old mode 100755 new mode 100644 similarity index 97% rename from apps/rfmnodered.py rename to apps/rcpulsegw.py index c325cb0..4b83990 --- a/apps/rfmnodered.py +++ b/apps/rcpulsegw.py @@ -40,6 +40,7 @@ class clientthread(threading.Thread): del self.__socket break try: + print(chunk) lock.acquire() d = json.loads(chunk) rctrx.send(d["protocol"], d["params"]) diff --git a/fs20tx.py b/fs20tx.py deleted file mode 100755 index 884deda..0000000 --- a/fs20tx.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python2.7 - -from raspyrfm import * -import sys -import time - -rfm = RaspyRFM(2, RFM69) #when using the RaspyRFM twin -#elif Rfm69.Test(0): -# rfm = Rfm69() #when using a single single 868 MHz RaspyRFM -#else: -# print "No RFM69 module found!" -# exit() - -rfm.set_params( - Freq = 868.350, - Datarate = 5.0, - TxPower = -10, - ModulationType = rfm69.OOK, - SyncPattern = [0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x38], - Preamble = 0 - ) - -data = [] -bitcnt = 0 - -def AddBit(bit): - global data - global bitcnt - if bit: - if ((len(data) * 8) - bitcnt) < 6: - data.append(0) - - data[bitcnt / 8] |= 0xE0 >> (bitcnt % 8) - - if (bitcnt % 8) > 2: - data[(bitcnt / 8) + 1] |= (0xE0 << (8 - (bitcnt % 8))) & 0xFF - - bitcnt += 6 - - else: - if ((len(data) * 8) - bitcnt) < 4: - data.append(0) - - data[bitcnt / 8] |= 0xC0 >> (bitcnt % 8) - if (bitcnt % 8) > 4: - data[(bitcnt / 8) + 1] |= (0xC0 << (8 - (bitcnt % 8))) & 0xFF - - bitcnt += 4 - -def AddByte(b): - mask = 0x80 - while (mask): - AddBit(b & mask) - mask >>= 1 - cnt = 0 - while b: - cnt += 1 - b &= b - 1 - AddBit(cnt & 1) - -def MakeFS20Frame(hc, adr, cmd): - q = 0 - AddByte(hc >> 8) - AddByte(hc & 0xFF) - q += hc >> 8 - q += hc & 0xFF - AddByte(adr) - q += adr - if (cmd > 0xFF): - AddByte(cmd >> 8) - q += cmd >> 8 - AddByte(cmd & 0xFF) - q += cmd & 0xFF - q += 6 - AddByte(q & 0xFF) - AddBit(0) - -data = [] -bitcnt = 0 -MakeFS20Frame(int(sys.argv[1], 0), int(sys.argv[2], 0), int(sys.argv[3], 0)) -print(data) -for x in range(3): - rfm.send_packet(data)