Page 4 of 16

Re: Read British Gas Hive Heating temperature

Posted: Tuesday 12 December 2017 10:28
by MikeF
Hi imcfarla,

Your code starts with

Code: Select all

import Domoticz
- can you point me to this library, please?

Re: Read British Gas Hive Heating temperature

Posted: Tuesday 12 December 2017 15:36
by imcfarla
it is built into the python plugin system.

This is not a script you can run using a script or cron job it needs to be installed as a plugin into domoticz.

Re: Read British Gas Hive Heating temperature

Posted: Tuesday 12 December 2017 22:55
by MikeF
Ah, OK - I haven't been using the python plugin system so far. I thought maybe I could use the Domoticz library in some of my other scripts.

Re: Read British Gas Hive Heating temperature

Posted: Wednesday 13 December 2017 0:01
by imcfarla
Further updates:
Now if you are running a system with TimedOut available (Revision 8651 or higher) should get notifications when the device is uncontactable
There is a new parameter for the port you are running domoticz on the localhost and you need to add 127.0.0.1 to your "Local Networks"
You will need to restart Domoticz if you update to this plugin version so you can see the new parameter.

Code: Select all

'''
<plugin key="HivePlug" name="Hive Plugin" author="imcfarla and MikeF" version="0.3" 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="6"/>
        <param field="Mode2" label="Domoticz Port" width="30px" required="true" default="8080"/>
        <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

class BasePlugin:
    enabled = False
    
    def __init__(self):
        self.sessionId = ''
        self.counter = 0
        self.multiplier = 10
        self.lightsSet = set()
        self.TimedOutAvailable = False
    
    def onStart(self):
        Domoticz.Log('Starting')
        if Parameters["Mode6"] == "Debug":
            Domoticz.Debugging(1)
        if self.getDomoticzRevision() >= 8651: 
            # Devices[unit].TimedOut only appears in Revision >= 8651
            self.TimedOutAvailable = True
            Domoticz.Log("TimedOut available")
        else:
            Domoticz.Log("TimedOut not available")
        self.multiplier = int(Parameters['Mode1'])
        self.counter = self.multiplier # update immediately
        if self.sessionId == '':
            Domoticz.Log('Creating Session')
            self.GetSessionID()
        Domoticz.Debug(self.sessionId)
        self.onHeartbeat()

    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 thermostatui["attributes"]["presence"]["reportedValue"] == "ABSENT":
                            if self.TimedOutAvailable:
                                Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                            else:
                                Domoticz.Log("Device Offline : " + Devices[unit].Name)
                        else:
                            if heating == 'ON':
                                if Devices[unit].nValue == 0:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                    else:
                                        Devices[unit].Update(nValue=1, sValue='On')
                            else:
                                if Devices[unit].nValue == 1:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                    else:
                                        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"]["presence"]["reportedValue"] == "ABSENT":
                                #Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            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':
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), TimedOut=0) # 2 = Set Level
                                        else:
                                            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 thermostatui["attributes"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            Devices[unit].Update(nValue=0, sValue='Off')
                                else:
                                    Domoticz.Debug("State: " + Devices[unit].sValue)
                                    if Devices[unit].nValue != 1:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                        else:
                                            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 GetSessionID(self):
            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']

    def GetDevices(self):
            nodes = False
            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 urllib.HTTPError as e:
                if e.code == 401: # Unauthorised - need new sessionId
                    self.onStop()
                    self.GetSessionID()
                    r = urlopen(req).read().decode('utf-8')
                else:
                    Domoticz.Log(str(e))
            try:
                nodes = json.loads(r)['nodes']
            except Exception as e:
                Domoticz.Log(str(e))
            return 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

    def getDomoticzRevision(self):
        Revision = 0
        url = 'http://127.0.0.1:' + Parameters['Mode2'] + '/json.htm?type=command&param=getversion'
        req = Request(url)
        try:
            r = urlopen(req).read().decode('utf-8')
            Revision = json.loads(r)['Revision']
        except Exception as e:
            if e.code == 401:
                Domoticz.Error("Ensure you have 127.0.0.1 in your 'Local Networks' selection")
            else:
                Domoticz.Error(str(e))
        return Revision

_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))

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



Re: Read British Gas Hive Heating temperature

Posted: Wednesday 13 December 2017 17:02
by imcfarla
A bit more debugging and the TimedOut stuff actually works

Hive takes a while to identify the device is offline but once it does...

Code: Select all

'''
<plugin key="HivePlug" name="Hive Plugin" author="imcfarla and MikeF" version="0.3.1" 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="6"/>
        <param field="Mode2" label="Domoticz Port" width="30px" required="true" default="8080"/>
        <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

class BasePlugin:
    enabled = False
    
    def __init__(self):
        self.sessionId = ''
        self.counter = 0
        self.multiplier = 10
        self.lightsSet = set()
        self.TimedOutAvailable = False
    
    def onStart(self):
        Domoticz.Log('Starting')
        if Parameters["Mode6"] == "Debug":
            Domoticz.Debugging(1)
        if int(self.getDomoticzRevision()) >= 8651: 
            # Devices[unit].TimedOut only appears in Revision >= 8651
            self.TimedOutAvailable = True
            Domoticz.Log("TimedOut available")
        else:
            Domoticz.Log("TimedOut not available")
        self.multiplier = int(Parameters['Mode1'])
        self.counter = self.multiplier # update immediately
        if self.sessionId == '':
            Domoticz.Log('Creating Session')
            self.GetSessionID()
        Domoticz.Debug(self.sessionId)
        self.onHeartbeat()

    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 thermostatui["attributes"]["presence"]["reportedValue"] == "ABSENT":
                            if self.TimedOutAvailable:
                                if Devices[unit].TimedOut == 0:
                                    Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                            else:
                                Domoticz.Log("Device Offline : " + Devices[unit].Name)
                        else:
                            if heating == 'ON':
                                if Devices[unit].nValue == 0:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                    else:
                                        Devices[unit].Update(nValue=1, sValue='On')
                            else:
                                if Devices[unit].nValue == 1:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                    else:
                                        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)
                            Domoticz.Debug(Devices[unit].Name + ": " + node["attributes"]["presence"]["reportedValue"])
                            if node["attributes"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            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':
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), TimedOut=0) # 2 = Set Level
                                        else:
                                            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"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            Devices[unit].Update(nValue=0, sValue='Off')
                                else:
                                    Domoticz.Debug("State: " + Devices[unit].sValue)
                                    if Devices[unit].nValue != 1:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                        else:
                                            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 GetSessionID(self):
            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']

    def GetDevices(self):
            nodes = False
            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 urllib.HTTPError as e:
                if e.code == 401: # Unauthorised - need new sessionId
                    self.onStop()
                    self.GetSessionID()
                    r = urlopen(req).read().decode('utf-8')
                else:
                    Domoticz.Log(str(e))
            try:
                nodes = json.loads(r)['nodes']
            except Exception as e:
                Domoticz.Log(str(e))
            return 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

    def getDomoticzRevision(self):
        Revision = 0
        url = 'http://127.0.0.1:' + Parameters['Mode2'] + '/json.htm?type=command&param=getversion'
        req = Request(url)
        try:
            r = urlopen(req).read().decode('utf-8')
            j = json.loads(r)
            Revision = j['Revision']
            Version = j['version']
            Domoticz.Debug("Domoticz Revision: " + str(Revision))
            Domoticz.Debug("Domoticz Version: " + Version + '->' + str(int(Version[-4:])))
            if int(Version[-4:]) > Revision:  # I've managed to create a build that has Version different to Revision so take the highest
                Revision = int(Version[-4:])
            Domoticz.Debug("Domoticz Revision: " + str(Revision))
        except urllib.HTTPError as e:
            if e.code == 401:
                Domoticz.Error("Ensure you have 127.0.0.1 in your 'Local Networks' selection")
            else:
                Domoticz.Error(str(e))
        except Exception as e:
                Domoticz.Error(str(e))
        return Revision

_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))

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



Re: Read British Gas Hive Heating temperature

Posted: Thursday 14 December 2017 22:37
by mark.sellwood
I'm on Beta V3.8785

I have just tried this out and i'm getting the following error:-

Code: Select all

2017-12-14 21:27:59.955 PluginSystem: Entering work loop.
2017-12-14 21:28:00.080 Error: (HivePlug) failed to load 'plugin.py', Python Path used was ':/usr/lib/python35.zip:/usr/lib/python3.5:/usr/lib/python3.5/plat-arm-linux-gnueabihf:/usr/lib/python3.5/lib-dynload'.
2017-12-14 21:28:00.080 Error: (Hive) Module Import failed, exception: 'ImportError'
2017-12-14 21:28:00.080 Error: (Hive) Module Import failed: ' Name: plugin'
2017-12-14 21:28:00.080 Error: (Hive) Error Line details not available.
Any idea what could be happening?

Re: Read British Gas Hive Heating temperature

Posted: Thursday 14 December 2017 23:23
by imcfarla
have you got python 3.5 installed?

it looks like a failure to load the plugin because of the initial imports

Re: Read British Gas Hive Heating temperature

Posted: Thursday 14 December 2017 23:33
by mark.sellwood
Yep python 3.5 is installed, I have other plugins running fine.
Do I need to install something for sys or json?

Re: Read British Gas Hive Heating temperature

Posted: Friday 15 December 2017 16:28
by imcfarla
I haven't got anything extra installed - debian on armhf with python 3.5.3-1

actually you can remove the 2 lines

Code: Select all

import sys
sys.path.append('/usr/lib/python3/dist-packages')
they are not needed anymore

Re: Read British Gas Hive Heating temperature

Posted: Friday 15 December 2017 19:25
by mark.sellwood
I have just done a clean install of the 2017-11-29-raspbian-stretch-lite
Installed python3-dev
Installed Domoticz
Done updatebeta
made a directory in domoticz/plugins named Hive
made a file named plugin.py and pasted your script in to it
restarted domoticz
when to hardware & added the new hardware
domoticz goes off line
I then removed the hive directory and restarted domoticz and I can see the original issue in the log.
I then installed 2 other python plugins and they both worked fine.
So it looks as if its something in the Hive script but as nothing useful gets logged I'm at a bit of a loss.

Re: Read British Gas Hive Heating temperature

Posted: Friday 15 December 2017 19:28
by sach
Script works fine for me but I'm running on a ubuntu vm and compiled domoticz from source. Not sure if that makes a difference?

Re: Read British Gas Hive Heating temperature

Posted: Friday 15 December 2017 19:29
by imcfarla
I had problems with beta and had to revert to stable until I compiled it myself

Re: Read British Gas Hive Heating temperature

Posted: Friday 15 December 2017 20:44
by mark.sellwood
Ok, I'll give the stable a try over the weekend.

Re: Read British Gas Hive Heating temperature

Posted: Saturday 30 December 2017 17:25
by Jem101
I've only just stumbled across this thread and having a Hive thermostat and a couple of bulbs, I thought I'd give it a try. I can confirm that the script causes the latest beta version of Domoticz to fail but running it on the latest stable release works perfectly. I actually have two RPis set up with Domoticz, my main one is running the beta releases and the secondary one, the stable release - both are on Rasbian Stretch and both are up to date.

I have to say that the Python plugin works perfectly for me, interrogating the status of the heating and the three bulbs I have and showing the results along with allowing me to control the heating from within Domoticz. I don't have any other Python plugins running on the 'beta' RPi so I cannot be sure if the issue is with this code or the plugin architecture itself, the error I see in the log, (once I remove the plugin.py file and restart the Domoticz service is exactly the same as posted by mark.sellwood above.

Annoying really, I don't want to go back to the stable version as I have some other things running which require the beta channel, but any hints, tips or advice as to what I can do to troubleshoot this would be appreciated.



EDIT : Just thought I'd try adding some other Python plugin and I get the same crash on the beta release so it does look as if the issue is with the plugin system and not with this particular code.

Re: Read British Gas Hive Heating temperature

Posted: Saturday 30 December 2017 17:41
by imcfarla
I am running beta compiled directly from git and everything works correctly. I can only assume that something in the beta binary published is conflicting with something on my system.

Having said that I have a few issues with the timers in beta...

Re: Read British Gas Hive Heating temperature

Posted: Monday 01 January 2018 16:25
by Calzor Suzay
imcfarla wrote: Wednesday 13 December 2017 17:02 A bit more debugging and the TimedOut stuff actually works

Hive takes a while to identify the device is offline but once it does...

Code: Select all

'''
<plugin key="HivePlug" name="Hive Plugin" author="imcfarla and MikeF" version="0.3.1" 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="6"/>
        <param field="Mode2" label="Domoticz Port" width="30px" required="true" default="8080"/>
        <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

class BasePlugin:
    enabled = False
    
    def __init__(self):
        self.sessionId = ''
        self.counter = 0
        self.multiplier = 10
        self.lightsSet = set()
        self.TimedOutAvailable = False
    
    def onStart(self):
        Domoticz.Log('Starting')
        if Parameters["Mode6"] == "Debug":
            Domoticz.Debugging(1)
        if int(self.getDomoticzRevision()) >= 8651: 
            # Devices[unit].TimedOut only appears in Revision >= 8651
            self.TimedOutAvailable = True
            Domoticz.Log("TimedOut available")
        else:
            Domoticz.Log("TimedOut not available")
        self.multiplier = int(Parameters['Mode1'])
        self.counter = self.multiplier # update immediately
        if self.sessionId == '':
            Domoticz.Log('Creating Session')
            self.GetSessionID()
        Domoticz.Debug(self.sessionId)
        self.onHeartbeat()

    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 thermostatui["attributes"]["presence"]["reportedValue"] == "ABSENT":
                            if self.TimedOutAvailable:
                                if Devices[unit].TimedOut == 0:
                                    Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                            else:
                                Domoticz.Log("Device Offline : " + Devices[unit].Name)
                        else:
                            if heating == 'ON':
                                if Devices[unit].nValue == 0:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                    else:
                                        Devices[unit].Update(nValue=1, sValue='On')
                            else:
                                if Devices[unit].nValue == 1:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                    else:
                                        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)
                            Domoticz.Debug(Devices[unit].Name + ": " + node["attributes"]["presence"]["reportedValue"])
                            if node["attributes"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            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':
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), TimedOut=0) # 2 = Set Level
                                        else:
                                            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"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                        else:
                                            Devices[unit].Update(nValue=0, sValue='Off')
                                else:
                                    Domoticz.Debug("State: " + Devices[unit].sValue)
                                    if Devices[unit].nValue != 1:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                        else:
                                            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 GetSessionID(self):
            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']

    def GetDevices(self):
            nodes = False
            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 urllib.HTTPError as e:
                if e.code == 401: # Unauthorised - need new sessionId
                    self.onStop()
                    self.GetSessionID()
                    r = urlopen(req).read().decode('utf-8')
                else:
                    Domoticz.Log(str(e))
            try:
                nodes = json.loads(r)['nodes']
            except Exception as e:
                Domoticz.Log(str(e))
            return 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

    def getDomoticzRevision(self):
        Revision = 0
        url = 'http://127.0.0.1:' + Parameters['Mode2'] + '/json.htm?type=command&param=getversion'
        req = Request(url)
        try:
            r = urlopen(req).read().decode('utf-8')
            j = json.loads(r)
            Revision = j['Revision']
            Version = j['version']
            Domoticz.Debug("Domoticz Revision: " + str(Revision))
            Domoticz.Debug("Domoticz Version: " + Version + '->' + str(int(Version[-4:])))
            if int(Version[-4:]) > Revision:  # I've managed to create a build that has Version different to Revision so take the highest
                Revision = int(Version[-4:])
            Domoticz.Debug("Domoticz Revision: " + str(Revision))
        except urllib.HTTPError as e:
            if e.code == 401:
                Domoticz.Error("Ensure you have 127.0.0.1 in your 'Local Networks' selection")
            else:
                Domoticz.Error(str(e))
        except Exception as e:
                Domoticz.Error(str(e))
        return Revision

_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))

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


Is this an official update to this code? :)

Re: Read British Gas Hive Heating temperature

Posted: Tuesday 02 January 2018 0:32
by imcfarla
How do you mean official?

Re: Read British Gas Hive Heating temperature

Posted: Tuesday 02 January 2018 0:45
by Calzor Suzay
It’s a change but not by the original poster.
Didn’t know if had been tweaked for their scenario or bettered overall.

Re: Read British Gas Hive Heating temperature

Posted: Wednesday 03 January 2018 0:01
by imcfarla
Slight updates
- Update the Hive url
- Use the Parameter[DomoticzVersion] now available to check if timedout is available
- Change the Domoticz Port to be optional (it is not used after version 3.8791 see above)
- Remove some of the imports that are not needed

Code: Select all

'''
<plugin key="HivePlug" name="Hive Plugin" author="imcfarla and MikeF" version="0.3.2" wikilink="http://www.domoticz.com/wiki/plugins/plugin.html" externallink="https://my.hivehome.com/login/">
    <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="6"/>
        <param field="Mode2" label="Domoticz Port - only needed prior to version 3.8791" width="40px" required="false" default="8080"/>
        <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 json
from urllib.request import Request, urlopen
from urllib.parse import urlencode

class BasePlugin:
    enabled = False
    
    def __init__(self):
        self.sessionId = ''
        self.counter = 0
        self.multiplier = 10
        self.lightsSet = set()
        self.TimedOutAvailable = False
    
    def onStart(self):
        Domoticz.Log('Starting')
        if Parameters["Mode6"] == "Debug":
            Domoticz.Debugging(1)
        if int(self.getDomoticzRevision()) >= 8651: 
            # Devices[unit].TimedOut only appears in Revision >= 8651
            self.TimedOutAvailable = True
            Domoticz.Log("TimedOut available")
        else:
            Domoticz.Log("TimedOut not available")
        self.multiplier = int(Parameters['Mode1'])
        self.counter = self.multiplier # update immediately
        if self.sessionId == '':
            Domoticz.Log('Creating Session')
            self.GetSessionID()
        Domoticz.Debug(self.sessionId)
        self.onHeartbeat()

    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 thermostatui["attributes"]["presence"]["reportedValue"] == "ABSENT":
                            if self.TimedOutAvailable:
                                if Devices[unit].TimedOut == 0:
                                    Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1)
                            else:
                                Domoticz.Log("Device Offline : " + Devices[unit].Name)
                        else:
                            if heating == 'ON':
                                if Devices[unit].nValue == 0:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=1, sValue='On', TimedOut=0)
                                    else:
                                        Devices[unit].Update(nValue=1, sValue='On')
                            else:
                                if Devices[unit].nValue == 1:
                                    if self.TimedOutAvailable:
                                        Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0)
                                    else:
                                        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:
                        rssi = 12*((0 - node["attributes"]["RSSI"]["reportedValue"])/100)
                        if node['id'] == Devices[unit].DeviceID:
                            if unit not in set(self.lightsSet):
                                self.lightsSet.add(unit)
                            Domoticz.Debug(Devices[unit].Name + ": " + node["attributes"]["presence"]["reportedValue"])
                            if node["attributes"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1, SignalLevel=0)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0, SignalLevel=int(rssi))
                                        else:
                                            Devices[unit].Update(nValue=0, sValue='Off', SignalLevel=int(rssi))
                                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':
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), TimedOut=0, SignalLevel=int(rssi)) # 2 = Set Level
                                        else:
                                            Devices[unit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), SignalLevel=int(rssi)) # 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()
                        #light_rssi = 12*((0 - node["attributes"]["RSSI"]["reportedValue"])/100)
                        if node["attributes"]["state"]["reportedValue"] == "OFF":
                            Devices[newUnit].Update(nValue=0, sValue='Off', SignalLevel=int(rssi))
                        else: 
                            Devices[newUnit].Update(nValue=2, sValue=str(node["attributes"]["brightness"]["reportedValue"]), SignalLevel=int(rssi)) # 2 = Set Level
            activeplugs = self.GetActivePlugs(d)
            if activeplugs:
                for node in activeplugs:
                    for unit in Devices:
                        rssi = 12*((0 - node["attributes"]["RSSI"]["reportedValue"])/100)
                        if node['id'] == Devices[unit].DeviceID:
                            if node["attributes"]["presence"]["reportedValue"] == "ABSENT":
                                if self.TimedOutAvailable:
                                    if Devices[unit].TimedOut == 0:
                                        Devices[unit].Update(nValue=Devices[unit].nValue, sValue=Devices[unit].sValue, TimedOut=1, SignalLevel=0)
                                else:
                                    Domoticz.Log("Device Offline : " + Devices[unit].Name)
                            else:
                                if node["attributes"]["state"]["reportedValue"] == "OFF":
                                    if Devices[unit].nValue != 0:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=0, sValue='Off', TimedOut=0, SignalLevel=int(rssi))
                                        else:
                                            Devices[unit].Update(nValue=0, sValue='Off', SignalLevel=int(rssi))
                                else:
                                    Domoticz.Debug("State: " + Devices[unit].sValue)
                                    if Devices[unit].nValue != 1:
                                        if self.TimedOutAvailable:
                                            Devices[unit].Update(nValue=1, sValue='On', TimedOut=0, SignalLevel=int(rssi))
                                        else:
                                            Devices[unit].Update(nValue=1, sValue='On', SignalLevel=int(rssi))
                            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', SignalLevel=int(rssi))
                        else:
                            Devices[unit].Update(nValue=1, sValue='On', SignalLevel=int(rssi))
        else:
            self.counter += 1
            Domoticz.Debug('Counter = ' + str(self.counter))

    def GetSessionID(self):
            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']

    def GetDevices(self):
            nodes = False
            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 urllib.HTTPError as e:
                if e.code == 401: # Unauthorised - need new sessionId
                    self.onStop()
                    self.GetSessionID()
                    r = urlopen(req).read().decode('utf-8')
                else:
                    Domoticz.Log(str(e))
            try:
                nodes = json.loads(r)['nodes']
            except Exception as e:
                Domoticz.Log(str(e))
            return 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

    def getDomoticzRevision(self):
        Revision = 0
        if 'DomoticzVersion' in Parameters:
            Domoticz.Log("DomoticzVersion Available")
            Revision = Parameters['DomoticzVersion'][-4:]
        else:
            Domoticz.Log("DomoticzVersion Not Available - Using JSON")
            url = 'http://127.0.0.1:' + Parameters['Mode2'] + '/json.htm?type=command&param=getversion'
            Domoticz.Log("Version URL: " + url)
            req = Request(url)
            try:
                r = urlopen(req).read().decode('utf-8')
                j = json.loads(r)
                Revision = j['Revision']
                Version = j['version']
                Domoticz.Debug("Domoticz Revision: " + str(Revision))
                Domoticz.Debug("Domoticz Version: " + Version + '->' + str(int(Version[-4:])))
                if int(Version[-4:]) > Revision:  # I've managed to create a build that has Version different to Revision so take the highest
                    Revision = int(Version[-4:])
            except urllib.HTTPError as e:
                if e.code == 401:
                    Domoticz.Error("Ensure you have 127.0.0.1 in your 'Local Networks' selection")
                else:
                    Domoticz.Error(str(e))
            except Exception as e:
                Domoticz.Error(str(e))
        Domoticz.Debug("Domoticz Revision: " + str(Revision))
        return Revision

_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))

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

Re: Read British Gas Hive Heating temperature

Posted: Thursday 04 January 2018 12:39
by Jem101
This is a really good plugin but I'm having an issue with the latest version (posted above on 2nd Jan)

I install the plugin OK and configure it but no devices are created and in the log I get the following

Code: Select all

2018-01-04 11:23:46.177 (Hive Devices) Initialized version 0.3.2, author 'imcfarla and MikeF' 
 2018-01-04 11:23:46.239 (Hive Devices) Starting 
 2018-01-04 11:23:46.240 (Hive Devices) Debug log level set to: 'true'. 
 2018-01-04 11:23:46.240 (Hive Devices) DomoticzVersion Not Available - Using JSON 
 2018-01-04 11:23:46.240 (Hive Devices) Version URL: http://127.0.0.1:8080/json.htm?type=command∂m=getversion 
 2018-01-04 11:23:46.360 Error: (Hive Devices) 'onStart' failed 'NameError'. 
 2018-01-04 11:23:46.361 Error: (Hive Devices) ----> Line 425 in /home/pi/domoticz/plugins/Hive/plugin.py, function onStart 
 2018-01-04 11:23:46.361 Error: (Hive Devices) ----> Line 36 in /home/pi/domoticz/plugins/Hive/plugin.py, function onStart 
 2018-01-04 11:23:46.361 Error: (Hive Devices) ----> Line 412 in /home/pi/domoticz/plugins/Hive/plugin.py, function getDomoticzRevision 
 2018-01-04 11:23:51.275 (Hive Devices) Deleting Session 
 2018-01-04 11:23:51.515 (Hive Devices) HTTP Error 401: Unauthorized 
 2018-01-04 11:23:51.588 (Hive Devices) Exiting work loop... 
 2018-01-04 11:23:51.876 (Hive Devices) Stopped. 
 2018-01-04 11:23:55.714 (Hive Devices) Calling message handler 'onHeartbeat'. 
 2018-01-04 11:23:55.715 (Hive Devices) onHeartbeat called 
 2018-01-04 11:23:55.715 (Hive Devices) Counter = 1 
 2018-01-04 11:24:05.693 (Hive Devices) Calling message handler 'onHeartbeat'. 
 2018-01-04 11:24:05.694 (Hive Devices) onHeartbeat called 
 2018-01-04 11:24:05.694 (Hive Devices) Counter = 2 
 2018-01-04 11:24:15.723 (Hive Devices) Calling message handler 'onHeartbeat'. 
 2018-01-04 11:24:15.723 (Hive Devices) onHeartbeat called 
 2018-01-04 11:24:15.723 (Hive Devices) Counter = 3  
which then repeats every ten counts on the heartbeat- as you can see I have turned on the debuting option to get the extra details.

It looks like an issue with the connection authorisation, the HTTP 401 error looks particularly suspicious!

If I go back to version 0.2 (so quite old) it works perfectly, I've cleaned out the pycache folder and made sure that I'm putting the right Hive credentials in.

Any ideas what might be causing this? I can't see any obvious mistakes I might have made when copying the code.

Many thanks in advance