Added in lights, plugs and control of the devices. Setting the temperature in Domoticz now updates Hive.
A side effect is that you can now set a light to be any percentage rather than just a multiple of 5 - wake up light here we come...
Only works for Active Light Dimmable as I haven't got any Cool to Warm or Colour Changing
ActivePlug should work but again I haven't got any so I can't test.
The Heartbeat multiplier defines how many heartbeats before it processes the data from Hive - I started off with 6 (60s) as the default but now I have added in control this really should just be 1 (10s) otherwise you wait too long before the dashboard updates to say you have switched on a light.
place this code in a folder called plugin.py in a folder called Hive in the plugins directory in your domoticz folder.
i.e.
restart domoticz <- important otherwise the plugin will not be detected.
Then go to setup and you should be able to choose Hive plugin in the Type dropdown.
Code: Select all
'''
<plugin key="HivePlug" name="Hive Plugin" author="imcfarla and mikef" version="0.2" wikilink="http://www.domoticz.com/wiki/plugins/plugin.html" externallink="https://www.google.com/">
<params>
<param field="Username" label="Hive Username" width="200px" required="true" default=""/>
<param field="Password" label="Hive Password" width="200px" required="true" default=""/>
<param field="Mode1" label="Heartbeat Multiplier" width="30px" required="true" default="1"/>
<param field="Mode6" label="Debug" width="75px">
<options>
<option label="True" value="Debug"/>
<option label="False" value="Normal" default="true" />
</options>
</param>
</params>
</plugin>
'''
import Domoticz
import sys
import json
sys.path.append('/usr/lib/python3/dist-packages')
from urllib.request import Request, urlopen
from urllib.parse import urlencode
def find_key_in_list(d, value):
if isinstance(d, list):
t = list(d)
for v in d:
if isinstance(v, dict):
p = find_key(v, value)
if not p:
t.remove(v)
return t
def find_key(d, value):
for (k, v) in d.items():
if isinstance(v, dict):
p = find_key(v, value)
if p:
return [k] + p
elif v == value:
return [k]
def merge_dicts(*dict_args):
'''
Given any number of dicts, shallow copy and merge into a new dict,
precedence goes to key value pairs in latter dicts.
'''
result = { }
for dictionary in dict_args:
result.update(dictionary)
return result
class BasePlugin:
enabled = False
def __init__(self):
self.sessionId = ''
self.counter = 0
self.multiplier = 10
self.lightsSet = set()
def onStart(self):
Domoticz.Log('Starting')
if Parameters["Mode6"] == "Debug":
Domoticz.Debugging(1)
self.multiplier = int(Parameters['Mode1'])
self.counter = self.multiplier #update immediately
if self.sessionId == '':
Domoticz.Log('Creating Session')
payload = {'username':Parameters["Username"], 'password':Parameters["Password"]}
headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
url = 'https://beekeeper-uk.hivehome.com/1.0/gateway/login'
req = Request(url, data = json.dumps(payload).encode('ascii'), headers = headers, unverifiable = True)
r = urlopen(req).read().decode('utf-8')
self.sessionId = json.loads(r)['token']
Domoticz.Debug(self.sessionId)
def onStop(self):
Domoticz.Log('Deleting Session')
headers = {'Content-Type': 'application/vnd.alertme.zoo-6.1+json', 'Accept': 'application/vnd.alertme.zoo-6.2+json', \
'X-AlertMe-Client': 'Hive Web Dashboard', 'X-Omnia-Access-Token': self.sessionId }
url = 'https://api.prod.bgchprod.info:443/omnia/auth/sessions/' + self.sessionId
req = Request(url, headers = headers)
req.get_method = lambda : 'DELETE'
try:
r = urlopen(req).read()
except Exception as e:
Domoticz.Log(str(e))
def onConnect(self, Connection, Status, Description):
Domoticz.Debug('onConnect called')
def onMessage(self, Connection, Data, Status, Extra):
Domoticz.Debug('onMessage called')
def onCommand(self, Unit, Command, Level, Hue):
Domoticz.Log('onCommand called for Unit ' + str(Unit) + ": Parameter '" + str(Command) + "', Level: " + str(Level))
Domoticz.Debug(str(Devices[Unit].Type))
Domoticz.Debug(str(Devices[Unit].SubType))
Domoticz.Debug(Devices[Unit].DeviceID)
Domoticz.Debug(str(Devices[Unit].sValue))
headers = {'Content-Type': 'application/vnd.alertme.zoo-6.2+json', 'Accept': 'application/vnd.alertme.zoo-6.2+json', \
'X-AlertMe-Client': 'swagger', 'X-Omnia-Access-Token': self.sessionId}
url = 'https://api.prod.bgchprod.info:443/omnia/nodes/' + Devices[Unit].DeviceID
if self.isLight(Unit):
Domoticz.Log("Setting Light Parameters")
if str(Command) == "Set Level":
payload = self.CreateLightPayload("ON", Level)
if str(Command) == "On":
payload = self.CreateLightPayload("ON", Devices[Unit].LastLevel)
if str(Command) == "Off":
payload = self.CreateLightPayload("OFF", Devices[Unit].LastLevel)
elif self.isThermostat(Unit):
payload = self.CreateThermostatPayload(Level)
elif self.isActivePlug(Unit):
payload = self.CreateActivePlugPayload(Level)
else:
payload = ""
Domoticz.Log("Unknown Device Type")
if payload != "":
req = Request(url, data = json.dumps(payload).encode('ascii'), headers = headers, unverifiable = True)
req.get_method = lambda : 'PUT'
try:
r = urlopen(req).read().decode('utf-8')
except Exception as e:
Domoticz.Log(str(e))
def onNotification(self, Name, Subject, Text, Status, Priority, Sound, ImageFile):
Domoticz.Debug('Notification: ' + Name + ',' + Subject + ',' + Text + ',' + Status + ',' + str(Priority) + ',' + Sound + ',' + ImageFile)
def onDisconnect(self, Connection):
Domoticz.Debug('onDisconnect called')
def onHeartbeat(self):
Domoticz.Debug('onHeartbeat called')
if self.counter >= self.multiplier:
foundInsideDevice = False
foundTargetDevice = False
foundHeatingDevice = False
foundThermostatDevice = False
Domoticz.Debug('Getting Data')
self.counter = 1
d = self.GetDevices()
Domoticz.Debug('Getting Temperatures')
thermostat = self.GetThermostat(d)
if thermostat:
# get the temperature and heating states
temp = thermostat["attributes"]["temperature"]["reportedValue"]
Domoticz.Debug('Temp = ' + str(temp))
targetTemp = thermostat["attributes"]["targetHeatTemperature"]["reportedValue"]
if targetTemp < 7.0: targetTemp = 7.0
Domoticz.Debug('Target = ' + str(targetTemp))
heating = thermostat["attributes"]["stateHeatingRelay"]["reportedValue"]
Domoticz.Debug('Heating = ' + heating)
Domoticz.Debug('Getting Battery Status')
thermostatui = self.GetThermostatUI(d)
# get the battery and rssi values
thermostat_battery = thermostatui["attributes"]["batteryLevel"]["reportedValue"]
Domoticz.Debug('Battery = ' + str(thermostat_battery))
thermostat_rssi = 12*((0 - thermostatui["attributes"]["RSSI"]["reportedValue"])/100)
Domoticz.Debug('RSSI = ' + str(thermostat_rssi))
# Loop through the devices and update temperatures
Domoticz.Debug('Updating Devices')
for unit in Devices:
if Devices[unit].DeviceID == "Hive_Inside":
Devices[unit].Update(nValue=int(temp), sValue=str(temp))
foundInsideDevice = True
if Devices[unit].DeviceID == "Hive_Target":
Devices[unit].Update(nValue=int(targetTemp), sValue=str(targetTemp))
foundTargetDevice = True
if Devices[unit].DeviceID == "Hive_Heating":
foundHeatingDevice = True
if heating == 'ON':
if Devices[unit].nValue == 0:
Devices[unit].Update(nValue=1, sValue='On')
else:
if Devices[unit].nValue == 1:
Devices[unit].Update(nValue=0, sValue='Off')
if Devices[unit].DeviceID == thermostat['id']:
foundThermostatDevice = True
if Devices[unit].Type == 242: #Thermostat
Devices[unit].Update(nValue = int(targetTemp), sValue = str(targetTemp), BatteryLevel = int(thermostat_battery), SignalLevel = int(thermostat_rssi))
if foundInsideDevice == False:
Domoticz.Device(Name = 'Inside', Unit = self.GetNextUnit(False), TypeName = 'Temperature', DeviceID = 'Hive_Inside').Create()
self.counter = self.multiplier
if foundTargetDevice == False:
Domoticz.Device(Name = 'Target', Unit = self.GetNextUnit(False), TypeName = 'Temperature', DeviceID = 'Hive_Target').Create()
self.counter = self.multiplier
if foundHeatingDevice == False:
Domoticz.Device(Name = 'Heating', Unit = self.GetNextUnit(False), TypeName = 'Switch', Switchtype = 0, DeviceID ='Hive_Heating').Create()
self.counter = self.multiplier
if foundThermostatDevice == False:
Domoticz.Device(Name = 'Thermostat', Unit = self.GetNextUnit(False), Type = 242, Subtype = 1, DeviceID = thermostat['id']).Create()
self.counter = self.multiplier
lights = self.GetLights(d)
if lights:
for node in lights:
for unit in Devices:
if node['id'] == Devices[unit].DeviceID:
if unit not in set(self.lightsSet):
self.lightsSet.add(unit)
if node["attributes"]["state"]["reportedValue"] == "OFF":
if Devices[unit].nValue != 0:
Devices[unit].Update(nValue=0, sValue='Off')
else:
Domoticz.Debug("State: " + Devices[unit].sValue)
Domoticz.Debug("Brightness Target: " + str(Devices[unit].LastLevel))
Domoticz.Debug("Brightness: " + str(node["attributes"]["brightness"]["reportedValue"]))
if Devices[unit].LastLevel != int(node["attributes"]["brightness"]["reportedValue"]) or Devices[unit].sValue == 'Off':
Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"])) # 2 = Set Level
break
else:
Domoticz.Log("Light not found " + node["name"])
newUnit = self.GetNextUnit(False)
Domoticz.Device(Name = node["name"], Unit = newUnit, Type=244, Subtype=73, Switchtype=7, DeviceID = node['id']).Create()
if node["attributes"]["state"]["reportedValue"] == "OFF":
Devices[newUnit].Update(nValue=0, sValue='Off')
else:
Devices[newUnit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"])) # 2 = Set Level
activeplugs = self.GetActivePlugs(d)
if activeplugs:
for node in activeplugs:
for unit in Devices:
if node['id'] == Devices[unit].DeviceID:
if node["attributes"]["state"]["reportedValue"] == "OFF":
if Devices[unit].nValue != 0:
Devices[unit].Update(nValue=0, sValue='Off')
else:
Domoticz.Debug("State: " + Devices[unit].sValue)
if Devices[unit].nValue != 1:
Devices[unit].Update(nValue=1, sValue='On')
break
else:
Domoticz.Log("ActivePlug not found " + node["name"])
newUnit = self.GetNextUnit(False)
Domoticz.Device(Name = node["name"], Unit = newUnit, Type=244, Subtype=73, Switchtype=0, DeviceID = node['id']).Create()
if node["attributes"]["state"]["reportedValue"] == "OFF":
Devices[newUnit].Update(nValue=0, sValue='Off')
else:
Devices[unit].Update(nValue=1, sValue='On')
else:
self.counter += 1
Domoticz.Debug('Counter = ' + str(self.counter))
def GetDevices(self):
headers = {'Content-Type': 'application/vnd.alertme.zoo-6.2+json', 'Accept': 'application/vnd.alertme.zoo-6.2+json', \
'X-AlertMe-Client': 'swagger', 'X-Omnia-Access-Token': self.sessionId}
url = 'https://api.prod.bgchprod.info:443/omnia/nodes'
req = Request(url, headers = headers, unverifiable = True)
try:
r = urlopen(req).read().decode('utf-8')
except Exception as e:
Domoticz.Log(str(e))
return json.loads(r)['nodes']
def GetThermostat(self, d):
x = find_key_in_list(d, 'http://alertme.com/schema/json/node.class.thermostat.json#')
if x:
k = 'stateHeatingRelay'
if k in x[0]['attributes']:
thermostat = x[0]
elif k in x[1]['attributes']:
thermostat = x[1]
else:
thermostat = False
else:
thermostat = False
return thermostat
def GetThermostatUI(self, d):
thermostatui = False
x = find_key_in_list(d, 'http://alertme.com/schema/json/node.class.thermostatui.json#')
if x:
thermostatui = x[0]
else: # Try a Hive2 thermostat
x = find_key_in_list(d,"Hive2")
if x:
thermostatui = x[0]
return thermostatui
def GetLights(self, d):
lights = False
x = find_key_in_list(d,"http://alertme.com/schema/json/node.class.light.json#")
if x:
lights = x
return lights
def GetActivePlugs(self, d):
plugs = False
x = find_key_in_list(d,"http://alertme.com/schema/json/node.class.smartplug.json#")
if x:
lights = x
return plugs
def GetNextUnit(self, unit):
if not unit:
nextUnit = len(Devices) + 1
else:
nextUnit = unit +1
if nextUnit in Devices or nextUnit <= 1:
nextUnit = self.GetNextUnit(nextUnit)
return nextUnit
def CreateLightPayload(self, State, Brightness):
response = {}
nodes = []
attributes = {}
state = {}
brightness = {}
brightness["targetValue"] = Brightness
state["targetValue"] = State
attributes["attributes"] = {"brightness":brightness,"state":state}
nodes.append(attributes)
response["nodes"] = nodes
return response
def CreateThermostatPayload(self, Temperature):
response = {}
nodes = []
attributes = {}
targetHeatTemperature = {}
targetHeatTemperature["targetValue"] = Temperature
attributes["attributes"] = {"targetHeatTemperature":targetHeatTemperature}
nodes.append(attributes)
response["nodes"] = nodes
return response
def CreateActivePlugPayload(self, State):
response = {}
nodes = []
attributes = {}
state = {}
state["targetValue"] = State
attributes["attributes"] = {"state":state}
nodes.append(attributes)
response["nodes"] = nodes
return response
def isLight(self, Unit):
Domoticz.Debug(str(self.lightsSet))
if Devices[Unit].Type == 244 and Devices[Unit].SubType == 73 and Unit in self.lightsSet:
return True
else:
return False
def isThermostat(self, Unit):
if Devices[Unit].Type == 242:
return True
else:
return False
def isActivePlug(self, Unit):
if Devices[Unit].Type == 244 and Devices[Unit].SubType == 73 and Unit not in self.lightsSet and Devices[Unit].DeviceID != "Hive_Heating":
return True
else:
return False
_plugin = BasePlugin()
def onStart():
_plugin.onStart()
def onStop():
_plugin.onStop()
def onConnect(Connection, Status, Description):
_plugin.onConnect(Connection, Status, Description)
def onMessage(Connection, Data, Status, Extra):
_plugin.onMessage(Connection, Data, Status, Extra)
def onCommand(Unit, Command, Level, Hue):
_plugin.onCommand(Unit, Command, Level, Hue)
def onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile):
_plugin.onNotification(Name, Subject, Text, Status, Priority, Sound, ImageFile)
def onDisconnect(Connection):
_plugin.onDisconnect(Connection)
def onHeartbeat():
_plugin.onHeartbeat()
def DumpConfigToLog():
for x in Parameters:
if Parameters[x] != '':
Domoticz.Debug("'" + x + "':'" + str(Parameters[x]) + "'")
Domoticz.Debug('Device count: ' + str(len(Devices)))
for x in Devices:
Domoticz.Debug('Device: ' + str(x) + ' - ' + str(Devices[x]))
Domoticz.Debug("Device ID: '" + str(Devices[x].ID) + "'")
Domoticz.Debug("Device Name: '" + Devices[x].Name + "'")
Domoticz.Debug('Device nValue: ' + str(Devices[x].nValue))
Domoticz.Debug("Device sValue: '" + Devices[x].sValue + "'")
Domoticz.Debug('Device LastLevel: ' + str(Devices[x].LastLevel))