Read British Gas Hive Heating temperature

In this subforum you can show projects you have made, or you are busy with. Please create your own topic.
MikeF
Posts: 145
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi
Domoticz version: V3.8153
Location: UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by MikeF » Tuesday 12 December 2017 11:28

Hi imcfarla,

Your code starts with

Code: Select all

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

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Tuesday 12 December 2017 16:36

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.

MikeF
Posts: 145
Joined: Sunday 19 April 2015 0:36
Target OS: Raspberry Pi
Domoticz version: V3.8153
Location: UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by MikeF » Tuesday 12 December 2017 23:55

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.

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Wednesday 13 December 2017 1:01

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



imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Wednesday 13 December 2017 18: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



mark.sellwood
Posts: 37
Joined: Tuesday 04 March 2014 11:33
Target OS: Raspberry Pi
Domoticz version:
Location: Surrey, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by mark.sellwood » Thursday 14 December 2017 23:37

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?
3 x Pi, 1 Master, 2 Slaves, 1x Aeotec Z-Stick S2, 4xSP103 PIR, 5xPowerNode 1, 1xSmart Energy Switch Gen5, 4xFGSS101 Smoke Sensor, 2xFGD212, 9xFGS212 , 7xFGS221/2, 1xAD142 , 1xTKB TZ68E , 2xAeotec Multi Sensor, 3 x NodOn CRC-3-1-00.

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Friday 15 December 2017 0:23

have you got python 3.5 installed?

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

mark.sellwood
Posts: 37
Joined: Tuesday 04 March 2014 11:33
Target OS: Raspberry Pi
Domoticz version:
Location: Surrey, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by mark.sellwood » Friday 15 December 2017 0:33

Yep python 3.5 is installed, I have other plugins running fine.
Do I need to install something for sys or json?
3 x Pi, 1 Master, 2 Slaves, 1x Aeotec Z-Stick S2, 4xSP103 PIR, 5xPowerNode 1, 1xSmart Energy Switch Gen5, 4xFGSS101 Smoke Sensor, 2xFGD212, 9xFGS212 , 7xFGS221/2, 1xAD142 , 1xTKB TZ68E , 2xAeotec Multi Sensor, 3 x NodOn CRC-3-1-00.

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Friday 15 December 2017 17:28

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

mark.sellwood
Posts: 37
Joined: Tuesday 04 March 2014 11:33
Target OS: Raspberry Pi
Domoticz version:
Location: Surrey, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by mark.sellwood » Friday 15 December 2017 20:25

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.
3 x Pi, 1 Master, 2 Slaves, 1x Aeotec Z-Stick S2, 4xSP103 PIR, 5xPowerNode 1, 1xSmart Energy Switch Gen5, 4xFGSS101 Smoke Sensor, 2xFGD212, 9xFGS212 , 7xFGS221/2, 1xAD142 , 1xTKB TZ68E , 2xAeotec Multi Sensor, 3 x NodOn CRC-3-1-00.

sach
Posts: 93
Joined: Wednesday 12 October 2016 14:33
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by sach » Friday 15 December 2017 20:28

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?

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Friday 15 December 2017 20:29

I had problems with beta and had to revert to stable until I compiled it myself

mark.sellwood
Posts: 37
Joined: Tuesday 04 March 2014 11:33
Target OS: Raspberry Pi
Domoticz version:
Location: Surrey, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by mark.sellwood » Friday 15 December 2017 21:44

Ok, I'll give the stable a try over the weekend.
3 x Pi, 1 Master, 2 Slaves, 1x Aeotec Z-Stick S2, 4xSP103 PIR, 5xPowerNode 1, 1xSmart Energy Switch Gen5, 4xFGSS101 Smoke Sensor, 2xFGD212, 9xFGS212 , 7xFGS221/2, 1xAD142 , 1xTKB TZ68E , 2xAeotec Multi Sensor, 3 x NodOn CRC-3-1-00.

Jem101
Posts: 56
Joined: Saturday 02 May 2015 21:26
Target OS: Raspberry Pi
Domoticz version: Release
Location: London, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by Jem101 » Saturday 30 December 2017 18:25

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.

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Saturday 30 December 2017 18:41

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...

Calzor Suzay
Posts: 130
Joined: Tuesday 08 July 2014 15:10
Target OS: Raspberry Pi
Domoticz version: 3.8153
Location: UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by Calzor Suzay » Monday 01 January 2018 17:25

imcfarla wrote:
Wednesday 13 December 2017 18: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? :)

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Tuesday 02 January 2018 1:32

How do you mean official?

Calzor Suzay
Posts: 130
Joined: Tuesday 08 July 2014 15:10
Target OS: Raspberry Pi
Domoticz version: 3.8153
Location: UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by Calzor Suzay » Tuesday 02 January 2018 1:45

It’s a change but not by the original poster.
Didn’t know if had been tweaked for their scenario or bettered overall.

imcfarla
Posts: 22
Joined: Monday 04 December 2017 14:18
Target OS: Linux
Domoticz version:
Contact:

Re: Read British Gas Hive Heating temperature

Post by imcfarla » Wednesday 03 January 2018 1:01

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

Jem101
Posts: 56
Joined: Saturday 02 May 2015 21:26
Target OS: Raspberry Pi
Domoticz version: Release
Location: London, UK
Contact:

Re: Read British Gas Hive Heating temperature

Post by Jem101 » Thursday 04 January 2018 13:39

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

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest