add pilota casa protokoll
This commit is contained in:
parent
95d94586db
commit
f5c5ac8d8c
5 changed files with 69 additions and 292 deletions
26
README.md
26
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 <housecode> <address> <command>
|
||||
```
|
||||
|
||||
## intertechno.py
|
||||
controlling remote control sockets
|
||||
```sh
|
||||
./rcpulse.py -p ittristate -o <HOUSECODE A-P> -g <GROUP 1-4> -u <CHANNEL 1-4> -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> <channel 1-4> 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
|
||||
|
|
178
apps/fs20.py
178
apps/fs20.py
|
@ -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])
|
|
@ -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):
|
||||
|
|
1
apps/rfmnodered.py → apps/rcpulsegw.py
Executable file → Normal file
1
apps/rfmnodered.py → apps/rcpulsegw.py
Executable file → Normal file
|
@ -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"])
|
83
fs20tx.py
83
fs20tx.py
|
@ -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)
|
Loading…
Reference in a new issue