Skip to content

Instantly share code, notes, and snippets.

@berland
Last active June 21, 2020 11:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save berland/ec762e4c555a167953829aece46ec4a3 to your computer and use it in GitHub Desktop.
Save berland/ec762e4c555a167953829aece46ec4a3 to your computer and use it in GitHub Desktop.
Calls a Hunter Douglas PowerView API to mode shades based on MQTT commands
# This code is released to the public domain.
import sys
import paho.mqtt.client as mqtt
import requests
import datetime
import time
import sys
mqtt_broker = "server"
mqtt_port = 1883
hubname = "hdpowerview.foo.bar.com"
topics = ["powerview/sleepingroom/"]
shadeid = 63778
commands = ["upper/set", "lower/set"]
states = ["upper/state", "lower/state"]
def on_connect(client, userdata, flags, rc):
print("mqtt2powerview: Connected to MQTT and hub: " + hubname)
sys.stdout.flush()
for topic in topics:
for command in commands:
client.subscribe(topic + command)
def on_message(client, userdata, msg):
print("mqtt2powerview: Got message " + str(msg.topic) + " " + str(msg.payload))
sys.stdout.flush()
payload = int(float(msg.payload))
if payload == 100: # 100 is interpreted as zero.. confusing
payload = 99
try:
if "upper" in msg.topic and "set" in msg.topic:
position = int(payload / 100.0 * 65536)
move_curtain("upper", position)
if "lower" in msg.topic and "set" in msg.topic:
position = int(payload / 100.0 * 65536)
move_curtain("lower", position)
except ValueError:
print("mqtt2powerview: ERROR: Unable to parse payload")
sys.stdout.flush()
pass
lowerposition = 0 # Global state variable...
upperposition = 0 # global state
def move_curtain(curtain, position):
print(
"mqtt2powerview: "
+ str(datetime.datetime.now())
+ "moving {} to pos {}".format(curtain, position)
)
sys.stdout.flush()
if curtain == "upper":
# this only works if we also supply the lower position..
body = {
"shade": {
"id": shadeid,
"positions": {
"position1": lowerposition,
"posKind1": 1,
"position2": int(position),
"posKind2": 2,
},
}
}
elif curtain == "lower":
body = {
"shade": {
"id": shadeid,
"positions": {
"position1": int(position),
"posKind1": 1,
"position2": upperposition,
"posKind2": 2,
},
}
}
r = requests.put("http://" + hubname + "/api/shades/" + str(shadeid), json=body)
print("mqtt2powerview: " + str(r.json()))
sys.stdout.flush()
def on_disconnect(client, userdata, rc):
if rc != 0:
print("mqtt2powerview: Unexpected MQTT disconnection. Will auto-reconnect")
sys.stdout.flush()
client = mqtt.Client()
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.connect(mqtt_broker, mqtt_port)
client.on_message = on_message
counter = 0
prematureloops = 0
lastprematureloop = -1
while True:
# Poll:
r = None
try:
# Allow only 3 seconds response time
r = requests.get("http://" + hubname + "/api/shades/" + str(shadeid), timeout=3)
except requests.exceptions.RequestException as e:
print(
"mqtt2powerview: Connection error to PowerView HUB, sleeping and retrying"
)
print(e)
sys.stdout.flush()
client.loop(20) # loop() is better than sleep, avoiding MQTT connection loss
if not r:
print("mqtt2powerview: Retrying http in 5 secs..")
sys.stdout.flush()
client.loop(5)
continue
if "shade" not in r.json():
# Hub maybe not ready, wait and try again
print("mqtt2powerview: mqtt2powerview not able to get shade, wait and retry")
sys.stdout.flush()
client.loop(20)
continue
positions = r.json()["shade"]["positions"]
if "position1" in positions: ## lower
pos1 = r.json()["shade"]["positions"]["position1"]
if int(pos1) != lowerposition or counter % 100 == 0:
client.publish(topics[0] + states[1], str(int(pos1 / 65536.0 * 100)))
lowerposition = int(pos1)
if "position2" in positions:
pos2 = r.json()["shade"]["positions"]["position2"]
if int(pos2) != upperposition or counter % 100 == 0:
client.publish(topics[0] + states[0], str(int(pos2 / 65536.0 * 100)))
upperposition = int(pos2)
# print("mqtt2powerview: heartbeat.. " + str(counter))
sys.stdout.flush()
start_looptime = time.time()
errorvalue = client.loop(
2
) ## Denne kan finne på å returnere før 2 sek har gått, da bør vi restarte
looptime = time.time() - start_looptime
if counter > 5 and looptime < 1.5: # in seconds
prematureloops += 1
print(
"MQTT loop() returned in only "
+ str(looptime)
+ " seconds, cumulative "
+ str(prematureloops)
+ ", error "
+ str(errorvalue)
)
lastprematureloop = counter
if prematureloops > 100:
print("More than 100 premature MQTT loops, restarting")
sys.exit(1)
# Reset prematureloops if they count occur too often
if (
int(counter) - int(lastprematureloop) > 10
): # a minute since it happened the last time
# print("Heartbeat, reset prematureloop, counter={}, lastprematureloop={}".format(counter, lastprematureloop))
prematureloops = 0
if not int(counter) % 10:
print(
"Heartbeat, counter={}, lastprematureloop={}".format(
counter, lastprematureloop
)
)
counter = counter + 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment