From c1e8b3191030989f099d3c3c9a0df9c447f9387d Mon Sep 17 00:00:00 2001 From: NilsGrunwald Date: Mon, 7 Jun 2021 17:03:43 +0200 Subject: [PATCH] initial commit from old development --- BSHGateWay.py | 236 ++++++++++++++++++++++++++++++ Dockerfile | 13 ++ example-cert.pem | 16 ++ example-key.pem | 28 ++++ linux-service/bsh-gateway.service | 17 +++ linux-service/easy_install.sh | 26 ++++ linux-service/easy_uninstall.sh | 18 +++ requirements.txt | 0 8 files changed, 354 insertions(+) create mode 100644 BSHGateWay.py create mode 100644 Dockerfile create mode 100644 example-cert.pem create mode 100644 example-key.pem create mode 100644 linux-service/bsh-gateway.service create mode 100644 linux-service/easy_install.sh create mode 100644 linux-service/easy_uninstall.sh create mode 100644 requirements.txt diff --git a/BSHGateWay.py b/BSHGateWay.py new file mode 100644 index 0000000..b14be22 --- /dev/null +++ b/BSHGateWay.py @@ -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() diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..df79c8f --- /dev/null +++ b/Dockerfile @@ -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" ] \ No newline at end of file diff --git a/example-cert.pem b/example-cert.pem new file mode 100644 index 0000000..02f2531 --- /dev/null +++ b/example-cert.pem @@ -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----- diff --git a/example-key.pem b/example-key.pem new file mode 100644 index 0000000..1eb14f7 --- /dev/null +++ b/example-key.pem @@ -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----- diff --git a/linux-service/bsh-gateway.service b/linux-service/bsh-gateway.service new file mode 100644 index 0000000..3b9c372 --- /dev/null +++ b/linux-service/bsh-gateway.service @@ -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 diff --git a/linux-service/easy_install.sh b/linux-service/easy_install.sh new file mode 100644 index 0000000..f7e4bd2 --- /dev/null +++ b/linux-service/easy_install.sh @@ -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" \ No newline at end of file diff --git a/linux-service/easy_uninstall.sh b/linux-service/easy_uninstall.sh new file mode 100644 index 0000000..9fad6c4 --- /dev/null +++ b/linux-service/easy_uninstall.sh @@ -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" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29