initial commit from old development
This commit is contained in:
236
BSHGateWay.py
Normal file
236
BSHGateWay.py
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#Bosch Home Automation <> MQTT Gateway
|
||||||
|
|
||||||
|
import requests, json, time
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
|
||||||
|
#Suppress http request warning
|
||||||
|
import urllib3
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
class BSH:
|
||||||
|
#BSH Settings
|
||||||
|
IP_SHC = "192.168.178.20"
|
||||||
|
baseurl = 'https://' + IP_SHC + ':8444'
|
||||||
|
url = baseurl + '/remote/json-rpc'
|
||||||
|
states = ["childLock","value","position","armActivationDelayTime","alarmActivationDelayTime",
|
||||||
|
"walkState","petImmunityState","quality","faults","triggers","actuators","remainingTimeUntilArmed",
|
||||||
|
"state","path","enabled","devices","path","runningStartTime","runningEndTime","operationMode",
|
||||||
|
"setpointTemperature","setpointTemperatureForLevelComfort","setpointTemperatureForLevelEco",
|
||||||
|
"schedule","ventilationMode","low","boostMode","summerMode","on","calibrated","movingTimeTopToBottom",
|
||||||
|
"movingTimeBottomToTop","level","operationState","deviceId","switchState","operationMode",
|
||||||
|
"powerConsumption","energyConsumption","delay"]
|
||||||
|
|
||||||
|
#Mosquitto Settings
|
||||||
|
client_name = "BSH_Mosquitto_Client"
|
||||||
|
host_name = "192.168.178.36"
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
AutomationRules = {}
|
||||||
|
DicAutomationRules = {}
|
||||||
|
|
||||||
|
Devices = {}
|
||||||
|
DicDevices = {}
|
||||||
|
|
||||||
|
Scenarios = {}
|
||||||
|
DicScenarios = {}
|
||||||
|
|
||||||
|
Rooms = {}
|
||||||
|
DicRooms = {}
|
||||||
|
|
||||||
|
client = mqtt.Client(client_name)
|
||||||
|
|
||||||
|
#get URL Headers
|
||||||
|
def getURLHeaders(self):
|
||||||
|
return {'Content-Type' : 'application/json'}
|
||||||
|
|
||||||
|
def getCertificate(self):
|
||||||
|
return ("./example-cert.pem","./example-key.pem")
|
||||||
|
|
||||||
|
#subscribe to Long Polling
|
||||||
|
def subscribe(self):
|
||||||
|
payload = [
|
||||||
|
{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"method":"RE/subscribe",
|
||||||
|
"id":"randomid",
|
||||||
|
"params": ["com/bosch/sh/remote/*", None]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return self.shc_request(payload)
|
||||||
|
|
||||||
|
#Start Long Polling and keep it 2 seconds open
|
||||||
|
def poll(self,pollId):
|
||||||
|
payload = [
|
||||||
|
{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"method":"RE/longPoll",
|
||||||
|
"id":"randomid",
|
||||||
|
"params": [pollId, 2]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return self.shc_request(payload)
|
||||||
|
|
||||||
|
#Post Polling data to SHC
|
||||||
|
def shc_request(self,payload):
|
||||||
|
postRequest = requests.post(self.url, data=json.dumps(payload), headers = self.getURLHeaders(),
|
||||||
|
verify = False, cert = self.getCertificate())
|
||||||
|
return json.loads(postRequest.text)[0]['result']
|
||||||
|
|
||||||
|
#Sent MQTT Debugging Message
|
||||||
|
def publishDebugMsg(self,msg):
|
||||||
|
self.client.publish("bsh/debug",msg,0,True)
|
||||||
|
|
||||||
|
#Get all BSH Decives
|
||||||
|
def getDevices(self):
|
||||||
|
getRequest = self.session.get("https://"+self.IP_SHC+":8444/smarthome/devices", verify=False,
|
||||||
|
cert = self.getCertificate(), headers = self.getURLHeaders())
|
||||||
|
return json.loads(getRequest.content)
|
||||||
|
|
||||||
|
|
||||||
|
#Get all BSH automation rules
|
||||||
|
def getAutomationRules(self):
|
||||||
|
getRequest = self.session.get("https://"+self.IP_SHC+":8444/smarthome/automation/rules", verify=False,
|
||||||
|
cert = self.getCertificate(), headers = self.getURLHeaders())
|
||||||
|
return json.loads(getRequest.content)
|
||||||
|
|
||||||
|
#Get all BSH scenarios
|
||||||
|
def getScenarios(self):
|
||||||
|
getRequest = self.session.get("https://"+self.IP_SHC+":8444/smarthome/scenarios", verify=False,
|
||||||
|
cert = self.getCertificate(), headers = self.getURLHeaders())
|
||||||
|
return json.loads(getRequest.content)
|
||||||
|
|
||||||
|
#Get all BSH rooms
|
||||||
|
def getRooms(self):
|
||||||
|
getRequest = self.session.get("https://"+self.IP_SHC+":8444/smarthome/rooms", verify=False,
|
||||||
|
cert = self.getCertificate(), headers = self.getURLHeaders())
|
||||||
|
contents = getRequest.content
|
||||||
|
return json.loads(contents)
|
||||||
|
|
||||||
|
#Generate a topic name for a device
|
||||||
|
def getTopicBase(self,dev):
|
||||||
|
topic = ""
|
||||||
|
if "roomId" in dev:
|
||||||
|
topic = "bsh/"+self.DicRooms[dev["roomId"]]["name"]+"/"+dev["name"].replace(" ","_")+"/"
|
||||||
|
else:
|
||||||
|
topic = "bsh/"+dev["name"].replace(" ","_")+"/"
|
||||||
|
return topic.replace("-","")
|
||||||
|
|
||||||
|
#update message for a single device
|
||||||
|
def publishDevice(self,id):
|
||||||
|
topicbase = self.getTopicBase(id)
|
||||||
|
getRequest = self.session.get("https://"+self.IP_SHC+":8444/smarthome/devices/"+id["id"]+"/services", verify=False,
|
||||||
|
cert = self.getCertificate(), headers = self.getURLHeaders())
|
||||||
|
data = json.loads(getRequest.content)
|
||||||
|
for id in data:
|
||||||
|
if "state" in id:
|
||||||
|
for s in self.states:
|
||||||
|
if s in id["state"]:
|
||||||
|
topic = topicbase+id["state"]["@type"]+"/"+s
|
||||||
|
payload = id["state"][s]
|
||||||
|
self.client.publish(topic,json.dumps(payload, indent=4, sort_keys=False),0,True)
|
||||||
|
self.publishDebugMsg(" Publishing topic: "+ topic + " Payload: "+ json.dumps(payload, indent=4, sort_keys=False))
|
||||||
|
|
||||||
|
#publish the initial status messages
|
||||||
|
def publishBSHStatus(self):
|
||||||
|
for x in self.Devices:
|
||||||
|
self.publishDevice(x)
|
||||||
|
|
||||||
|
#react to MQTT scenario requests:
|
||||||
|
def onMqttMessage(self, client, userdata, message):
|
||||||
|
#currently only react on trigger="ON"
|
||||||
|
if (str(message.payload.decode("utf-8")) == "ON"):
|
||||||
|
self.client.publish(message.topic,"OFF", 0,True)
|
||||||
|
for s in self.Scenarios:
|
||||||
|
topic = "bsh/scenarios/"+s["name"].replace(" ","_")+"/trigger"
|
||||||
|
if (topic == message.topic):
|
||||||
|
self.publishDebugMsg(" Scenario triggered: "+s["name"])
|
||||||
|
url = self.baseurl + "/smarthome/scenarios/"+s["id"]+"/triggers"
|
||||||
|
postRequest = requests.post(url, data="", headers = self.getURLHeaders(),
|
||||||
|
verify = False, cert = self.getCertificate())
|
||||||
|
data = json.loads(postRequest.text)[0]['result']
|
||||||
|
if "errorCode" in data:
|
||||||
|
self.publishDebugMsg("Error occured during Scenario Call:")
|
||||||
|
self.publishDebugMsg(data["errorCode"])
|
||||||
|
|
||||||
|
#subscribe to message to trigger scenarios
|
||||||
|
def subscribeToScenariosRequests(self):
|
||||||
|
for s in self.Scenarios:
|
||||||
|
topic = "bsh/scenarios/"+s["name"].replace(" ","_")+"/trigger"
|
||||||
|
self.publishDebugMsg("Adding subscribe: "+topic)
|
||||||
|
self.client.publish(topic,"OFF", 0,True)
|
||||||
|
self.client.subscribe(topic,0)
|
||||||
|
self.client.on_message = self.onMqttMessage
|
||||||
|
|
||||||
|
|
||||||
|
#set up the system: login to MQTT + get all needed data from the BSH
|
||||||
|
def __init__(self):
|
||||||
|
self.client.connect(self.host_name)
|
||||||
|
self.publishDebugMsg("Connecting to MQTT Server")
|
||||||
|
self.publishDebugMsg("Setting up the Environment...")
|
||||||
|
|
||||||
|
self.publishDebugMsg("Getting Room information")
|
||||||
|
self.Rooms = self.getRooms()
|
||||||
|
self.publishDebugMsg(str(len(self.Rooms))+" rooms found:")
|
||||||
|
for x in self.Rooms:
|
||||||
|
self.publishDebugMsg(x["id"]+":"+x["name"])
|
||||||
|
self.DicRooms.update({x["id"]:x})
|
||||||
|
|
||||||
|
self.publishDebugMsg("Getting Devices")
|
||||||
|
self.Devices = self.getDevices()
|
||||||
|
self.publishDebugMsg (str(len(self.Devices))+" Devices found:")
|
||||||
|
for x in self.Devices:
|
||||||
|
if "roomId" in x:
|
||||||
|
self.publishDebugMsg(x["id"]+":"+x["name"]+":"+self.DicRooms[x["roomId"]]["name"])
|
||||||
|
else:
|
||||||
|
self.publishDebugMsg(x["id"]+":"+x["name"])
|
||||||
|
|
||||||
|
self.DicDevices.update({x["id"]:x})
|
||||||
|
|
||||||
|
self.publishDebugMsg("Getting Automation Rules")
|
||||||
|
self.AutomationRules = self.getAutomationRules()
|
||||||
|
self.publishDebugMsg(str(len(self.AutomationRules))+" Automation rules found:")
|
||||||
|
for x in self.AutomationRules:
|
||||||
|
self.publishDebugMsg(x["id"]+":"+x["name"])
|
||||||
|
self.DicAutomationRules.update({x["id"]:x})
|
||||||
|
|
||||||
|
self.publishDebugMsg("Getting Senarios")
|
||||||
|
self.Scenarios = self.getScenarios()
|
||||||
|
self.publishDebugMsg (str(len(self.Scenarios))+" Scenarios found:")
|
||||||
|
for x in self.Scenarios:
|
||||||
|
self.publishDebugMsg(x["id"]+":"+x["name"])
|
||||||
|
self.DicScenarios.update({x["id"]:x})
|
||||||
|
|
||||||
|
self.publishDebugMsg("Posting initial states to MQTT Server")
|
||||||
|
self.publishBSHStatus()
|
||||||
|
|
||||||
|
self.publishDebugMsg("Subscribe to Service Requests")
|
||||||
|
self.subscribeToScenariosRequests()
|
||||||
|
|
||||||
|
|
||||||
|
def loop(self):
|
||||||
|
self.publishDebugMsg("Subscribing to Bosch Smart Home System...")
|
||||||
|
pollId = self.subscribe()
|
||||||
|
self.publishDebugMsg(pollId)
|
||||||
|
self.publishDebugMsg("Starting Polling...")
|
||||||
|
#Restart Long Polling every 5 seconds
|
||||||
|
while True:
|
||||||
|
result = self.poll(pollId)
|
||||||
|
if result != []:
|
||||||
|
for e in result:
|
||||||
|
if "deviceId" in e:
|
||||||
|
if e["deviceId"] in self.DicDevices:
|
||||||
|
self.publishDebugMsg("status change for: "+self.DicDevices[e["deviceId"]]["name"])
|
||||||
|
self.publishDevice(self.DicDevices[e["deviceId"]])
|
||||||
|
else:
|
||||||
|
self.publishDebugMsg("Unknown message received:")
|
||||||
|
self.publishDebugMsg('\n------------------------------------------------------ %d -\n' % len(result))
|
||||||
|
self.publishDebugMsg(json.dumps(e, indent=4, sort_keys=False))
|
||||||
|
self.client.loop_start()
|
||||||
|
time.sleep(5)
|
||||||
|
self.client.loop_stop()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BoschSmartHome = BSH()
|
||||||
|
BoschSmartHome.loop()
|
||||||
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#$ docker build -t my-python-app .
|
||||||
|
#$ docker run -it --rm --name my-running-app my-python-app
|
||||||
|
|
||||||
|
FROM python:3
|
||||||
|
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
COPY requirements.txt ./
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
CMD [ "python", "./BSHGateWay.py" ]
|
||||||
16
example-cert.pem
Normal file
16
example-cert.pem
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICkTCCAXkCBFmSY3YwDQYJKoZIhvcNAQELBQAwDTELMAkGA1UEBhMCVVMwHhcN
|
||||||
|
MTcwODE1MDI1OTAyWhcNMTgwODE1MDI1OTAyWjANMQswCQYDVQQGEwJVUzCCASIw
|
||||||
|
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIZyQlYQUCkFq8KUMlRysRDkC5zH
|
||||||
|
UZpZr4De4kVCQ10LMZeQLLsYtK/uPD71cNgzdoTNnDt1jHlDS8qJvq2c9yLXOO4l
|
||||||
|
R+1vvz9GEUo5apVhWoJ2/rvmvQgVwGIa98o1s+Veh+EZlAKlmxuPuVK44ewpPxFD
|
||||||
|
8fcEZzoCa4XiF14VrQrQAmC6/yWYk0QmQBCAx97UIE5XaLtZq8zhRTkhIHigMMQs
|
||||||
|
5SLAmzCQTD1fxjbjNmCrWFmpJ6c7oodmwWvO6ciYS/IOemnSoViQxiPq+Y2iIGYy
|
||||||
|
eP9JIF6eqljaMR9MQ9klwsBOlDTeUP7r5n7K6cD7wYjmK7nY8kxyYi6eR48CAwEA
|
||||||
|
ATANBgkqhkiG9w0BAQsFAAOCAQEAPk9wfZSJBP31Tc5azEcRNUNxiFj473mGdxXp
|
||||||
|
hsZAGrS/Pte0x+OGHhLDD2hkidIDhLrvbGN6OMTRtSouvuNYMSYbRuzfd8pe+0op
|
||||||
|
dsuj/4+utK2MXE11C7L29ygYbPHIN/pn6jbSx3B8N+O/NsDLUuCOqzfp9EB8iQio
|
||||||
|
6CplRrMb+cVj8MEmSD8qguAjUroozBunuxce72M82tyifRTByCx8xYwdRr8FGXfz
|
||||||
|
1na4WzvnBh05hv0ywNzklgkoTKZk2Jw1G733PvNJ423IXBkFzDc2NIml++5St8xw
|
||||||
|
Hu1IC1GIDzvaxuVMqF6ixLG8QGRXBKtpJgzFEY6qG/TkIeONBg==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
example-key.pem
Normal file
28
example-key.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCGckJWEFApBavC
|
||||||
|
lDJUcrEQ5Aucx1GaWa+A3uJFQkNdCzGXkCy7GLSv7jw+9XDYM3aEzZw7dYx5Q0vK
|
||||||
|
ib6tnPci1zjuJUftb78/RhFKOWqVYVqCdv675r0IFcBiGvfKNbPlXofhGZQCpZsb
|
||||||
|
j7lSuOHsKT8RQ/H3BGc6AmuF4hdeFa0K0AJguv8lmJNEJkAQgMfe1CBOV2i7WavM
|
||||||
|
4UU5ISB4oDDELOUiwJswkEw9X8Y24zZgq1hZqSenO6KHZsFrzunImEvyDnpp0qFY
|
||||||
|
kMYj6vmNoiBmMnj/SSBenqpY2jEfTEPZJcLATpQ03lD+6+Z+yunA+8GI5iu52PJM
|
||||||
|
cmIunkePAgMBAAECggEAZ7URWIPi4ZHfSQu5uwxxaz8NehUB7FcMGxNSZOxVPBtb
|
||||||
|
WLc82eGX1zGkxUfckNk5rf1Qa0kkX5G6j/Qq4o72z6hG4ORfFFcjpfItehzKC4p6
|
||||||
|
H7MckeLNo8Prj4GP7Cn6p46Ar/FkC5qlB+CYqqe0lc/HN1E6/zklS0j8mdyp+8cM
|
||||||
|
aFx+unY/TDIrXMreIgSYr2Ys5XO2kN22pP+0Uf+OZic2+9EQOkIImvHpX6FEVw5j
|
||||||
|
iXfGOYjwliiAtU0Ojso78ey5R/7SCRgpX2y3SCznquq0weVuqE0CrxecAlMgZRn8
|
||||||
|
DklCNeaB0OytDKIfix5Q0yNKQThul98MPGgpDKC1oQKBgQC61WPO5XGhCEc2r8UB
|
||||||
|
RkcSb792Tik/+JaOhZqwiZkx3jnBmU0HAb9VplOT1ciy4Kc4VilvwXNTbcUPx9KK
|
||||||
|
2UUxPt1S8G4sfVBvgPSG0NWeFoCCMV+UxT7VpBfJTXUCygooy8w/GwLXeAhOAIpz
|
||||||
|
F/Aj9Gz4F1YjsYG+Js7y7vxosQKBgQC4OAVfCDpNsJOLkY7csihbHhz+KohO7iOP
|
||||||
|
giNF5YI6XKbaRARX6UeLnE2KkqF+CJWfO9REDcPT6kG/EHjsjYVGqHxUfm2B4DO7
|
||||||
|
F/0JdE/5Smd8z7MxdDZDO7+TNMrtmpuQu57MGbk44mDAAXA954/M795PUVomxPx9
|
||||||
|
6r3/einEPwKBgDcwV5ZDIoid6GNYEoqo1s+0YMsylW7HILoi7yncy3r2mPr+LMm4
|
||||||
|
E2vagO+3g9yLDfpPQVg4vbdUQpTBwwiu24iLeFdKnFDaB4uYfSLhx2g2X2mV6hUJ
|
||||||
|
GuGC4l/dWIYlZlDcuo2djf5V/6YC9OLAnHgSeKnkQtayVY/06MbMH5VRAoGBALEw
|
||||||
|
URpDE7E+MeyAqOTmB6L8p+5ggpNIwrN5/OtyAXyZOXOfEH5uRv6l7H9o4iQTpbZv
|
||||||
|
GZALnVvraimYcnc0+AgqbsvmfvX47Ej8ncnGMlYZlsiaDkV2/epVQcMZeEZp+0+O
|
||||||
|
5wJxi5KHS3/i4k7ot4vq++1W1luMBUAn1XAx1JKJAoGBALN9hB2P0/36dyIUv2e9
|
||||||
|
cGT2PO8I60aGNgCvQMADfDlfxTlquHAWJwu88IuzDLvG22sgserNMo3kyCRUCxLp
|
||||||
|
cJZF+bSxKu2lVlbN9JJwBZSaz00l80xbYcjBIh9UhcXqKkeGbZhqF3nZ+C9VtFbD
|
||||||
|
sIxIhyn924TNCEb3FzebpYKq
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
17
linux-service/bsh-gateway.service
Normal file
17
linux-service/bsh-gateway.service
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=BoschHomeAutomation 2 MQTT Gateway
|
||||||
|
After=multi-user.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=idle
|
||||||
|
Restart=always
|
||||||
|
RestartSec=30
|
||||||
|
StartLimitInterval=200
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
WorkingDirectory=/opt/bsh-gateway
|
||||||
|
ExecStart=/opt/bsh-gateway/BSHGateWay.py
|
||||||
|
ExecStop=/bin/sleep 1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
26
linux-service/easy_install.sh
Normal file
26
linux-service/easy_install.sh
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
mac=`cat /sys/class/net/$(ip route get 8.8.8.8 | sed -n 's/.* dev \([^ ]*\).*/\1/p')/address`
|
||||||
|
arch=`uname -m`
|
||||||
|
|
||||||
|
### installing bsh gateway
|
||||||
|
echo -e "\033[36m Installing BSH Gateway.\033[0m"
|
||||||
|
|
||||||
|
if [ -d "/opt/bsh-gateway" ]; then
|
||||||
|
systemctl stop bsh-gateway.service
|
||||||
|
cp BSHGateWay.py /opt/bsh-gateway/
|
||||||
|
cp example-key.pem /opt/bsh-gateway/
|
||||||
|
cp example-cert.pem /opt/bsh-gateway/
|
||||||
|
else
|
||||||
|
mkdir /opt/bsh-gateway
|
||||||
|
cp BSHGateWay.py /opt/bsh-gateway/
|
||||||
|
cp example-key.pem /opt/bsh-gateway/
|
||||||
|
cp example-cert.pem /opt/bsh-gateway/
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp bsh-gateway.service /lib/systemd/system/
|
||||||
|
chmod 644 /lib/systemd/system/bsh-gateway.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable bsh-gateway.service
|
||||||
|
systemctl start bsh-gateway.service
|
||||||
|
|
||||||
|
echo -e "\033[32m Installation completed. BSH 2 MQTT Gateway is now up and running.\033[0m"
|
||||||
18
linux-service/easy_uninstall.sh
Normal file
18
linux-service/easy_uninstall.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
cd /tmp
|
||||||
|
|
||||||
|
echo -e "\033[36m BSH GateWay Service.\033[0m"
|
||||||
|
systemctl stop bsh-gateway.service
|
||||||
|
|
||||||
|
|
||||||
|
### Uninstalling BSH GateWay
|
||||||
|
echo -e "\033[36m Uninstalling BSH GateWay.\033[0m"
|
||||||
|
|
||||||
|
rm -rf /opt/bsh-gateway/
|
||||||
|
|
||||||
|
systemctl disable bsh-gateway.service
|
||||||
|
rm /lib/systemd/system/bsh-gateway.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
|
||||||
|
echo -e "\033[32m Uninstall complete!\033[0m"
|
||||||
0
requirements.txt
Normal file
0
requirements.txt
Normal file
Reference in New Issue
Block a user