""" Copyright (C) 2014, Jaguar Land Rover This program is licensed under the terms and conditions of the Mozilla Public License, version 2.0. The full text of the Mozilla Public License is at https://www.mozilla.org/MPL/2.0/ Maintainer: Rudolf Streif (rstreif@jaguarlandrover.com) """ """ Thingcontrol (HomeLake) Server. """ import os, threading, base64 import time, httplib, json, math from urlparse import urlparse from rvijsonrpc import RVIJSONRPCServer import settings logger = None service_edge = None transaction_id = 0 # Thingcontrol Callback Server class ThingcontrolCallbackServer(threading.Thread): """ RPC server thread responding to Thingcontrol callbacks from the RVI framework """ def __init__(self, _logger, _service_edge): global logger global service_edge logger = _logger service_edge = _service_edge threading.Thread.__init__(self) self.init_callback_server() self.register_services() def init_callback_server(self): # initialize RPC server and register callback functions url = urlparse(settings.TC_SERVER_CALLBACK_URL) self.localServer = RVIJSONRPCServer(addr=((url.hostname, url.port)), logRequests=False) self.localServer.register_function(getDeviceStatus, settings.TC_SERVER_SERVICE_ID + "/getdevicestatus") self.localServer.register_function(setHueLighting, settings.TC_SERVER_SERVICE_ID + "/sethuelighting") self.localServer.register_function(setOutlet, settings.TC_SERVER_SERVICE_ID + "/setoutlet") self.localServer.register_function(setSwitch, settings.TC_SERVER_SERVICE_ID + "/setswitch") self.localServer.register_function(setLock, settings.TC_SERVER_SERVICE_ID + "/setlock") self.localServer.register_function(setDimmer, settings.TC_SERVER_SERVICE_ID + "/setdimmer") self.localServer.register_function(setThermostat, settings.TC_SERVER_SERVICE_ID + "/setthermostat") self.localServer.register_function(secureHome, settings.TC_SERVER_SERVICE_ID + "/securehome") def register_services(self): # register services with RVI framework result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/getdevicestatus', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: getdevicestatus service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/sethuelighting', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: sethuelighting service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/setoutlet', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setoutlet service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/setswitch', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setswitch service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/setlock', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setlock service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/setdimmer', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setdimmer service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/setthermostat', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setthermostat service name: %s', result['service']) result = service_edge.register_service(service = settings.TC_SERVER_SERVICE_ID + '/securehome', network_address = settings.TC_SERVER_CALLBACK_URL) logger.info('Thingcontrol Service Registration: setthermostat service name: %s', result['service']) def run(self): self.localServer.serve_forever() def shutdown(self): self.localServer.shutdown() self.localServer.server_close() # Callback functions def getDeviceStatus(devices, sendto): """ Return the status of the home automation devices. :param: devices: list of devices (wildcards ok) :param: sendto: RVI service to send the response to """ logger.info('Thingcontrol Callback Server: getDeviceStatus: devices: %s, sento: %s.', devices, sendto) if '*' in devices or 'thermostat' in devices: data = getThingcontrolStatus('thermostat') else: logger.warning('Thingcontrol Callback Server: getDeviceStatus: unknown device') return {u'status': 1} if data == None: logger.warning('Thingcontrol Callback Server: getDeviceStatus: no status information received') return {u'status': 1} sendRVIMessage(sendto, data) return {u'status': 0} def setHueLighting(deviceid, control): """ Control hue lighting device. :param: deviceid: id of the hue lighting device :param: control: hue settings """ logger.info('Thingcontrol Callback Server: setHueLighting: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zb_hue_bulb' data['msgTyp'] = 'zb_hue_msg' data['control'] = control sendThingcontrolCommand('huelighting', data) return {u'status': 0} def setOutlet(deviceid, control): """ Control wall outlet device. :param: deviceid: id of the outlet device :param: control: state """ logger.info('Thingcontrol Callback Server: setOutlet: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zb-smartplug' data['msgTyp'] = 'zb_smartplug_msg' data['control'] = control sendThingcontrolCommand('wallsmartoutlet', data) return {u'status': 0} def setSwitch(deviceid, control): """ Control switch device. :param: deviceid: id of the switch device :param: control: state """ logger.info('Thingcontrol Callback Server: setSwitch: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zb-smartswitch' data['msgTyp'] = 'zb_smartswitch_msg' data['control'] = control sendThingcontrolCommand('smartswitch', data) return {u'status': 0} def setLock(deviceid, control): """ Control door lock device. :param: deviceid: id of the lock device :param: control: state """ logger.info('Thingcontrol Callback Server: setLock: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zb_door_lock' data['msgTyp'] = 'zb_door_msg' data['control'] = control sendThingcontrolCommand('doorlock', data) return {u'status': 0} def setDimmer(deviceid, control): """ Control dimmer device. :param: deviceid: id of the dimmer device :param: control: state """ logger.info('Thingcontrol Callback Server: setDimmer: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zb-dimmer' data['msgTyp'] = 'zb_dimmer_msg' data['control'] = control sendThingcontrolCommand('dimmer', data) return {u'status': 0} def setThermostat(deviceid, control): """ Control thermostat device. :param: deviceid: id of the thermostat device :param: control: state """ logger.info('Thingcontrol Callback Server: setThermostat: deviceid: %s, control: %s.', deviceid, control) data = initData() data['device_id'] = deviceid data['device_type'] = 'zw_thermostat' data['msgTyp'] = 'zw_thermostat_msg' data['control'] = control sendThingcontrolCommand('thermostat', data) return {u'status': 0} def secureHome(deviceid, control): """ Control thermostat device. :param: deviceid: id of the thermostat device :param: control: state """ logger.info('Thingcontrol Callback Server: secureHome: deviceid: %s, control: %s.', deviceid, control) for c in control: if 'value' in c: if c['value'] == 'arm': switchLights('off') lockDoors('lock') elif c['value'] == 'disarm': switchLights('on') lockDoors('unlock') return {u'status': 0} def initData(): """ Initialize data structure for Thingcontrol message return: data structure in JSON format """ data = { "strId": "", "device_id": "", "device_type": "", "display_name": "none", "msgId": "", "msgTyp": "", "time": "", "loc": [{}], "pyld": [{}], "control": [{}] } return data def switchLights(state): """ Turn on/off all lights in the smarthome. :param: state: 'on' or 'off' """ data = initData() data['device_id'] = 'wip_gw2.zb_hue01' data['device_type'] = 'zb_hue_bulb' data['msgTyp'] = 'zb_hue_msg' if state == "off": data['control'] = [{"value":0, "controlR":0, "controlG":0, "controlB":0}] else: data['control'] = [{"value":255, "controlR":255, "controlG":255, "controlB":255}] sendThingcontrolCommand('huelighting', data) def lockDoors(state): """ Lock/unlock all doors in the smarthome :param: state: 'lock' or 'unlock' """ data = initData() data['device_id'] = 'wip_gw2.zb_lock01' data['device_type'] = 'zb_door_lock' data['msgTyp'] = 'zb_door_msg' data['control'] = [{"state":state}] sendThingcontrolCommand('doorlock', data) def sendThingcontrolCommand(command, data): """ Connect to the Thingcontrol server and send a command. :param: command: command to send :param: data: JSON data blob for command """ logger.info('Thingcontrol Callback Server: sendThingcontrolCommand: command: %s, data: %s, dest: %s.', command, data, settings.TC_SERVER_GATEWAY_URL) try: url = urlparse(settings.TC_SERVER_GATEWAY_URL) con = httplib.HTTPConnection(url.hostname, url.port) path = settings.TC_SERVER_GATEWAY_DOMAIN_CONTROL + '/' + command headers = { 'Content-Type':'application/json', 'Accept':'application/json'} con.request('POST', path, json.dumps(data), headers) res = con.getresponse() logger.info('Thingcontrol Callback Server: sendThingcontrolCommand: Response: %s %s', res.status, res.reason) except Exception as e: logger.error('Thingcontrol Callback Server: sendThingcontrolCommand: Exception: %s', e) return data def getThingcontrolStatus(command): """ Connect to the Thingcontrol Server and get the termostat status information. :param: command: the status command """ logger.info('Thingcontrol Callback Server: getThingcontrolStatus: command: %s, dest: %s.', command, settings.TC_SERVER_GATEWAY_URL) try: url = urlparse(settings.TC_SERVER_GATEWAY_URL) con = httplib.HTTPConnection(url.hostname, url.port) path = settings.TC_SERVER_GATEWAY_DOMAIN_STATUS + '/' + command con.request('GET', path) res = con.getresponse() data = json.loads(res.read()) except Exception as e: logger.error('Thingcontrol Callback Server: getThingcontrolStatus: Exception: %s', e) data = None return data old_temp = 0 def setIVIHVAC(): data = getThingcontrolStatus('thermostat') if data == None: return False for c in data['control']: if 'target_temp' in c: temp = c['target_temp'] if temp != old_temp: control = {} control['temp_front_left'] = temp control['temp_front_right'] = temp control['fan_speed'] = 5 sendIVI(settings.IVI_SERVICE_EDGE_URL, control) return True def sendRVIMessage(sendto, message): """ Send message to recipient via RVI. :param: sendto: recipient RVI service :param: meessage: message as RVI parameter block """ logger.info('Thingcontrol Callback Server: sending message: %s to %s', message, sendto) # send message try: service_edge.message(service_name = sendto, timeout = int(time.time()) + settings.RVI_SEND_TIMEOUT, parameters = [message]) except Exception as e: logger.error('Thingcontrol Callback Server: cannot send message: %s', e) return False logger.info('Thingcontrol Callback Server: successfully sent message: to %s', sendto) return True