Ueberschalter: Unterschied zwischen den Versionen

Aus C3MAWiki
(→‎Skript: replaced code with pre)
(→‎Skript: Updated with more debug code and inserted "Werkstatt mode")
Zeile 125: Zeile 125:
== Skript ==
== Skript ==
<pre>
<pre>
#!/usr/bin/python
import mosquitto, os, socket, time
import mosquitto, os, socket, time
from threading import Thread
from threading import Thread
from time import gmtime, strftime


#File: /usr/local/sbin/light2mqtt.py
#File: /usr/local/sbin/light2mqtt.py
Zeile 134: Zeile 136:
BUFFER_SIZE = 1024
BUFFER_SIZE = 1024


mqttBroker="10.23.42.31"
# Prometheus
#mqttBroker="10.23.42.31"
# BigBrother
mqttBroker="10.23.42.10"


mypid = os.getpid()
mypid = os.getpid()
Zeile 140: Zeile 145:


client = mosquitto.Mosquitto("RoomLights"+str(mypid))
client = mosquitto.Mosquitto("RoomLights"+str(mypid))
# Zeit in Sekunden, in der die Lamen nicht geschaltet werden koennen (Schutz der Lampen und relais)
LAMPSLEEPTIME=5


pollerState = True
pollerState = True
oldStates = None
oldStates = {}
t = None
t = None
lampSleepProtection = {}


def processState(ls):
def processState(ls):
lightnr=1
lightnr=1
global oldStates
global oldStates
global lampSleepProtection


werkstattState = "off"
for light in ls:
for light in ls:
if oldStates is None or light != oldStates[lightnr - 1]:
lightState = "on" if int(light) == 1 else "off"
lightState = "on" if int(light) == 1 else "off"
if str(lightnr) in oldStates and lightState != oldStates[str(lightnr)]:
client.publish("/room/light/%s/state" % lightnr, lightState, 0, True)
client.publish("/room/light/%s/state" % lightnr, lightState, 0, True)
print("%s Updated %s to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), lightnr, lightState) )
# Zeit festlegen, solange die Lampe nicht mehr geschalten werden darf
lampSleepProtection[light] = int(time.time()) + LAMPSLEEPTIME
# Mindestens eine Lampe in der Werkstatt muss an sein
if lightnr == 5 or lightnr == 6:
werkstattState = "on" if lightState == "on" else werkstattState
lightnr+=1
lightnr+=1
oldStates = ls
 
# Aenderungen in der Werkstatt kommunizieren:
if 'w' in oldStates and werkstattState != oldStates['w']:
client.publish("/room/light/w/state", werkstattState, 0, True)
print("%s Updated Werkstatt state to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), werkstattState) )


# Light ist das licht 1-6
# Light ist das licht 1-6
# State ist h oder l
# State ist h oder l


def switchLight(light, state):  
def switchLight(lights, state):  
global lampSleepProtection
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.connect((TCP_IP, TCP_PORT))
 
s.send("ollpew%s%s" % (light, state))
for l in lights:
 
if (l in lampSleepProtection and int(time.time()) < int(lampSleepProtection[l])):
time.sleep(0.2)
print("%s Mqtt Command blocked for Lamp %s for %s seconds" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), l, str(lampSleepProtection[l] - int(time.time()) ) ))
else:
# Zeit festlegen, solange die Lampe nicht mehr geschalten werden darf
lampSleepProtection[l] = int(time.time()) + LAMPSLEEPTIME
s.send("ollpew%s%s" % (l, state))
time.sleep(0.2)


data = s.recv(BUFFER_SIZE)
data = s.recv(BUFFER_SIZE)
Zeile 171: Zeile 199:


data = data.replace("\r", "").split("\n")
data = data.replace("\r", "").split("\n")
 
if len(data) > 3:
lightState = data[3].split(" ")[1]
lightState = data[3].split(" ")[1]
print("%s Proccess via Mqtt for Lamp%s to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(lights), state) )
processState(lightState)
processState(lightState)
else:
print("%s No answer from uC for Lamp%s to %s; only %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(lights), state, str(data)) )




Zeile 189: Zeile 219:
          
          
data = data.replace("\r", "").split("\n")
data = data.replace("\r", "").split("\n")
 
if len(data) > 2:
        lightState = data[2].split(" ")[1]
lightState = data[2].split(" ")[1]
 
print("%s Process by background thread" % strftime("%Y-%m-%d %H:%M:%S", gmtime()))
        processState(lightState)
processState(lightState)
else:
print("%s No answer from uC getting its status; only %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(data)) )


def on_connect(rc):
def on_connect(rc):
     #print("Connected with result code "+str(rc))
     #print("Connected with result code "+str(rc))
     client.subscribe("/room/light/+/command")
     client.subscribe("/room/light/+/command")
    client.subscribe("/room/light/+/state")
     pollerState = True
     pollerState = True


def on_message(userdata, msg):
def on_message(userdata, msg):
 
global oldStates
topic = msg.topic.strip("/").split("/")
topic = msg.topic.strip("/").split("/")


lightNr = int(topic[2])
lightNr = str(topic[2])
message = str(msg.payload)
message = str(msg.payload)


if lightNr >= 1 and lightNr <= 6 and message in ["on", "off"]:
if (topic[3] == "command"):
lightState = "l" if message == "off" else "h"
if (lightNr == "w" or int(lightNr) >= 1 and int(lightNr) <= 6) and message in ["on", "off"]:
switchLight(lightNr, lightState)
lightState = "l" if message == "off" else "h"
# Lichter in Array gruppieren fuer die Werkstatt
if (lightNr == "w"):
lights = [5, 6]
else: # Sonst array mit einem Licht erstellen
lights = [ int(lightNr) ]
switchLight(lights, lightState)
elif (topic[3] == "state"):
# Nicht die Werkstatt aktualieren, das passiwert in getStates()
try:
if (lightNr == "w" or int(lightNr) >= 1 and int(lightNr) <= 6):
oldStates[lightNr] = message
except ValueError:
pass


def on_disconnect(userdata):
def on_disconnect(userdata):
Zeile 218: Zeile 264:
if pollerState:
if pollerState:
getStates()
getStates()
time.sleep(15)  
# Update the button only all half minutes
time.sleep(30)  
          
          


Zeile 239: Zeile 286:
# print "Reconnecting..."
# print "Reconnecting..."
time.sleep(60)
time.sleep(60)
</pre>
</pre>



Version vom 9. Juni 2015, 20:04 Uhr

Dies ist ein Projekt, an dem momentan aktiv gearbeitet wird. Wenn du Fragen hast oder mitarbeiten möchtest, wende dich an Ollo.


Publikationen

Server

Hardware

Das ioBoard wird über das UART des Raspberry Pis angesprochen. Die Kommunikation zum Raspberry erfolgt über Ethernet, es werden keine weiteren Schnittstellen benötigt.

Software

  • raspbian (wheezy)
    • ser2net
    • lighttpd (optional für Webclient)

Konfiguration

In der Konfigurationsdatei /etc/ser2net.conf von ser2net aktiviert man folgende Konfiguration. Alle weiteren können auskommentiert werden:

2001:raw:120:/dev/ttyAMA0:9600 NONE 1STOPBIT 8DATABITS -XONXOFF -LOCAL -RTSCTS

Webserver konfigurieren

$ cd /var/
$ sudo chown pi:pi www/
$ cd cd /var/www/
$ git clone https://github.com/C3MA/uberschalter
$ cd uberschalter/web/
$ make

Aktualiseren der lighttpd Konfiguration zu folgender (Muss ggf. erst installiert werden):

server.modules = (
	"mod_cgi",
	"mod_access",
	"mod_alias",
	"mod_compress",
 	"mod_redirect",
#       "mod_rewrite",
)

#server.document-root        = "/var/www"
server.document-root        = "/var/www/uberschalter/web/www"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80


index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny             = ( "~", ".inc" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

compress.cache-dir          = "/var/cache/lighttpd/compress/"
compress.filetype           = ( "application/javascript", "text/css", "text/html", "text/plain" )

# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"

$HTTP["url"] =~ "/cgi-bin/" {
        cgi.assign = ( "" => "" )
}

cgi.assign      = (
        ".cgi"  => ""
)

Client

Android

Web

Auf dem RaspberryPi läuft auch Webserver um die Lampen zu steuern: (Zu erreichen im Raum-Netzwerk unter lampi oder 10.23.42.140.)

Lampenpositionen

Mqtt

Doku

Folgende Stati werden in MQTT geschickt:

/room/light/3/command off
/room/light/1/state on
/room/light/2/state on
/room/light/3/state off
/room/light/4/state on
/room/light/5/state on
/room/light/6/state on
/room/light/7/state on
/room/light/8/state on
/room/light/3/command off
/room/light/3/command off
/room/light/3/command on
/room/light/3/state on

Linux Befehl:

$ mosquitto_sub -v -h 10.23.42.31 -t "/room/light/#"

Eine Lampe kann mit folgenden Schema geschalten werden: /room/light/<id>/command (on|off).

Unter Linux wurde das wie folgt getestet:

$ mosquitto_pub -h 10.23.42.31 -t "/room/light/3/command" -m "off"
$ mosquitto_pub -h 10.23.42.31 -t "/room/light/3/command" -m "on"

Skript

#!/usr/bin/python
import mosquitto, os, socket, time
from threading import Thread
from time import gmtime, strftime

#File: /usr/local/sbin/light2mqtt.py

TCP_IP = '127.0.0.1'
TCP_PORT = 2001
BUFFER_SIZE = 1024

# Prometheus
#mqttBroker="10.23.42.31"
# BigBrother
mqttBroker="10.23.42.10"

mypid = os.getpid()


client = mosquitto.Mosquitto("RoomLights"+str(mypid))

# Zeit in Sekunden, in der die Lamen nicht geschaltet werden koennen (Schutz der Lampen und relais)
LAMPSLEEPTIME=5

pollerState = True
oldStates = {}
t = None
lampSleepProtection = {}

def processState(ls):
	lightnr=1
	global oldStates
	global lampSleepProtection

	werkstattState = "off"
	for light in ls:
		lightState = "on" if int(light) == 1 else "off"
		if str(lightnr) in oldStates and lightState != oldStates[str(lightnr)]:
			client.publish("/room/light/%s/state" % lightnr, lightState, 0, True)
			print("%s Updated %s to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), lightnr, lightState) )
			# Zeit festlegen, solange die Lampe nicht mehr geschalten werden darf
			lampSleepProtection[light] = int(time.time()) + LAMPSLEEPTIME

			# Mindestens eine Lampe in der Werkstatt muss an sein
		if lightnr == 5 or lightnr == 6:
			werkstattState = "on" if lightState == "on" else werkstattState
		lightnr+=1

	# Aenderungen in der Werkstatt kommunizieren:
	if 'w' in oldStates and werkstattState != oldStates['w']:
		client.publish("/room/light/w/state", werkstattState, 0, True)
		print("%s Updated Werkstatt state to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), werkstattState) )

# Light ist das licht 1-6
# State ist h oder l

def switchLight(lights, state): 
	global lampSleepProtection
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect((TCP_IP, TCP_PORT))
	
	for l in lights:
		if (l in lampSleepProtection and int(time.time()) < int(lampSleepProtection[l])):
			print("%s Mqtt Command blocked for Lamp %s for %s seconds" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), l, str(lampSleepProtection[l] - int(time.time()) ) ))
		else:
			# Zeit festlegen, solange die Lampe nicht mehr geschalten werden darf
			lampSleepProtection[l] = int(time.time()) + LAMPSLEEPTIME
			s.send("ollpew%s%s" % (l, state))
			time.sleep(0.2)

	data = s.recv(BUFFER_SIZE)
	s.close()

	data = data.replace("\r", "").split("\n")
	if len(data) > 3:
		lightState = data[3].split(" ")[1]
	 	print("%s Proccess via Mqtt for Lamp%s to %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(lights), state) )
		processState(lightState)
	else:
	 	print("%s No answer from uC for Lamp%s to %s; only %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(lights), state, str(data)) )


def getStates():
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((TCP_IP, TCP_PORT))

        s.send("ollpera")

        time.sleep(0.2)

        data = s.recv(BUFFER_SIZE)
        s.close()
        
	data = data.replace("\r", "").split("\n")
	if len(data) > 2:
		lightState = data[2].split(" ")[1]
		print("%s Process by background thread" % strftime("%Y-%m-%d %H:%M:%S", gmtime()))
		processState(lightState)
	else:
		print("%s No answer from uC getting its status; only %s" % (strftime("%Y-%m-%d %H:%M:%S", gmtime()), str(data)) )

def on_connect(rc):
    #print("Connected with result code "+str(rc))
    client.subscribe("/room/light/+/command")
    client.subscribe("/room/light/+/state")
    pollerState = True

def on_message(userdata, msg):
	global oldStates
	topic = msg.topic.strip("/").split("/")

	lightNr = str(topic[2])
	message = str(msg.payload)

	if (topic[3] == "command"):
		if (lightNr == "w" or int(lightNr) >= 1 and int(lightNr) <= 6) and message in ["on", "off"]:
			lightState = "l" if message == "off" else "h"
			# Lichter in Array gruppieren fuer die Werkstatt
			if (lightNr == "w"):
				lights = [5, 6]
			else: # Sonst array mit einem Licht erstellen
				lights = [ int(lightNr) ]
			switchLight(lights, lightState)
	elif (topic[3] == "state"):
		# Nicht die Werkstatt aktualieren, das passiwert in getStates()
		try:
			if (lightNr == "w" or int(lightNr) >= 1 and int(lightNr) <= 6):
				oldStates[lightNr] = message
		except ValueError:
			pass

def on_disconnect(userdata):
	pollerState = False
	#print "Disconnect"

def statePoller():
	while True:
		if pollerState:
			getStates()
		# Update the button only all half minutes
		time.sleep(30) 
        


client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect

client.will_set('/room/light/daemon/state', 'offline', 0, True)

t = Thread(target=statePoller)
t.start()

while True:
#	print "Connect..."
	client.connect(mqttBroker, 1883, 60, True)
	client.publish('/room/light/daemon/state', 'online', 0, True)
	while client.loop() == 0:
		pass
#	print "Reconnecting..."
	time.sleep(60)


Autostart im Raspberry: Folgende Zeile in /etc/rc.local einfügen: (vor dem exit 0 wenn vorhanden)

# Start the monster deamon to rule the world
/usr/bin/python /usr/local/sbin/light2mqtt.py &