/connector/ApiConnector.py

https://gitlab.com/farmee-public/farmee-controller-rpi · Python · 269 lines · 188 code · 52 blank · 29 comment · 37 complexity · 45f83bbe6d9372b3e1639b88eb8a27fe MD5 · raw file

  1. # -*- coding: utf-8 -*-
  2. #
  3. # farmee controller
  4. # Copyright (C) 2018 farmee GmbH
  5. #
  6. # This file is part of farmee controller.
  7. #
  8. # farmee controller is free software: you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License as published
  10. # by the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # farmee controller is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with farmee controller. If not, see <http://www.gnu.org/licenses/>.
  20. from __future__ import unicode_literals
  21. import json
  22. import logging
  23. import socket
  24. import urllib2
  25. import warnings
  26. from urllib2 import URLError, HTTPError
  27. import ssl
  28. class ApiConnector(object):
  29. def __init__(self):
  30. self.port = 443
  31. self.token = ''
  32. self.baseAddress = ''
  33. self.httpMode = 'https'
  34. self.timeout = 5.0
  35. def setTimeout(self, t):
  36. self.timeout = t
  37. return
  38. def init(self, baseAddress, apikey, port=443):
  39. self.baseAddress = baseAddress
  40. self.port = port
  41. logging.info("connecting to " + baseAddress + ":" + port)
  42. # authorize
  43. url = self.httpMode + '://' + self.baseAddress + ':' + str(port) + '/v1/oauth/token?apikey=' + apikey
  44. logging.debug( "call %s", url )
  45. headers = { "Content-Type": "application/json" }
  46. req = urllib2.Request( url=url, headers=headers )
  47. token = False
  48. try:
  49. content = urllib2.urlopen(req, timeout=self.timeout ).read()
  50. logging.info("content %s", content)
  51. jsonObject = json.loads( content )
  52. token = jsonObject["access_token"]
  53. except HTTPError as e:
  54. logging.warning( 'http error %s', e.code)
  55. except URLError as e:
  56. logging.warning( 'url error %s', e.reason)
  57. except socket.timeout as e:
  58. logging.warning( 'socket timeout' )
  59. if( token == False ):
  60. logging.error( 'authorization failed' )
  61. return False
  62. self.accessToken = token
  63. return True
  64. def __getApiUrl(self, request=''):
  65. return self.httpMode + '://' + self.baseAddress + ":" + str(self.port) + '/v1/' + request + '?access_token=' + self.accessToken
  66. def testConnection(self):
  67. connected = False
  68. try:
  69. urllib2.urlopen(self.__getApiUrl('configurations'), timeout=self.timeout).read()
  70. connected = True
  71. except (URLError, socket.timeout) as e:
  72. logging.warning('Unable to connect to farmee server')
  73. logging.warning(e)
  74. return connected
  75. def registerUnit(self,uuid,platform,ip,type,version):
  76. """
  77. Registers the Unit in the API and gets the Unit ID for further Calls
  78. """
  79. url = self.__getApiUrl('units/register')
  80. logging.debug( 'call ' + url )
  81. unitId = False
  82. data = json.dumps( { 'uuid': uuid, 'operatingSystem': platform,
  83. 'ipAddress': ip, 'controllerType': type,
  84. 'controllerVersion': version } )
  85. headers = { "Content-Type": "application/json" }
  86. req = urllib2.Request( url=url, data=data, headers=headers )
  87. try:
  88. content = urllib2.urlopen(req, timeout=self.timeout ).read()
  89. logging.info("content %s", content)
  90. jsonObject = json.loads( content )
  91. unit = jsonObject[0]
  92. unitId = unit["id"]
  93. except HTTPError as e:
  94. logging.warning( 'http error %s', e.code)
  95. except URLError as e:
  96. logging.warning( 'url error %s', e.reason)
  97. except socket.timeout as e:
  98. logging.warning( 'socket timeout' )
  99. return unitId
  100. # sends the current unitstate to the controlserver and gets the new configuration
  101. def updateState(self,data):
  102. url = self.__getApiUrl('units/sendstate')
  103. logging.debug( 'call ' + url )
  104. logging.debug("data %s", data )
  105. config = False
  106. data = json.dumps( data )
  107. headers = { "Content-Type": "application/json" }
  108. req = urllib2.Request( url=url, data=data, headers=headers )
  109. try:
  110. content = urllib2.urlopen(req, timeout=self.timeout ).read()
  111. logging.info("content %s", content)
  112. jsonObject = json.loads( content )
  113. config = jsonObject["configuration"]
  114. except HTTPError as e:
  115. logging.warning( 'http error %s', e.code)
  116. except URLError as e:
  117. logging.warning( 'url error %s', e.reason)
  118. except socket.timeout as e:
  119. logging.warning( 'socket timeout' )
  120. return config
  121. def getConfigurations(self,unitId):
  122. url = self.__getApiUrl( 'units/' + str(unitId) + '/configurations')
  123. logging.debug('Calling %s', url)
  124. req = urllib2.Request(url=url)
  125. try:
  126. content = urllib2.urlopen(req, timeout=self.timeout).read()
  127. logging.info("Retrieving sensor/actor configuration")
  128. logging.debug(content)
  129. return json.loads(content)
  130. except HTTPError as e:
  131. logging.warning('http error %s', e.code)
  132. except URLError as e:
  133. logging.warning('url error %s', e.reason)
  134. except socket.timeout as e:
  135. logging.warning('socket timeout')
  136. return False
  137. def saveSensor(self, sensor):
  138. url = self.__getApiUrl('sensors')
  139. logging.debug('Calling %s', url)
  140. headers = {"Content-Type": "application/json"}
  141. data = {
  142. 'name': sensor['name'],
  143. 'type': sensor['type'],
  144. 'config': sensor['config']
  145. }
  146. # update existing sensor if possible
  147. if 'id' in sensor:
  148. data['id'] = sensor['id']
  149. req = urllib2.Request(url=url, data=json.dumps(data), headers=headers)
  150. try:
  151. content = urllib2.urlopen(req, timeout=self.timeout).read()
  152. logging.info("Saving sensor %s", content)
  153. return json.loads(content)
  154. except HTTPError as e:
  155. logging.warning('http error %s', e.code)
  156. except URLError as e:
  157. logging.warning('url error %s', e.reason)
  158. except socket.timeout as e:
  159. logging.warning('socket timeout')
  160. def deleteSensor(self, sensor_id):
  161. url = self.__getApiUrl('sensors/{}'.format(sensor_id))
  162. logging.debug('Calling %s', url)
  163. req = urllib2.Request(url=url)
  164. req.get_method = lambda: 'DELETE'
  165. try:
  166. response = urllib2.urlopen(req, timeout=self.timeout).read()
  167. logging.info("Deleting sensor %s", response)
  168. return json.loads(response)
  169. except HTTPError as e:
  170. logging.warning('http error %s', e.code)
  171. except URLError as e:
  172. logging.warning('url error %s', e.reason)
  173. except socket.timeout as e:
  174. logging.warning('socket timeout')
  175. def saveActor(self, actor):
  176. url = self.__getApiUrl('actors')
  177. logging.debug('Calling %s', url)
  178. headers = {"Content-Type": "application/json"}
  179. data = {
  180. 'name': actor['name'],
  181. 'type': actor['type'],
  182. 'config': actor['config']
  183. }
  184. # update existing sensor if possible
  185. if 'id' in actor:
  186. data['id'] = actor['id']
  187. req = urllib2.Request(url=url, data=json.dumps(data), headers=headers)
  188. try:
  189. content = urllib2.urlopen(req, timeout=self.timeout).read()
  190. logging.info("Saving actor %s", content)
  191. return json.loads(content)
  192. except HTTPError as e:
  193. logging.warning('http error %s', e.code)
  194. except URLError as e:
  195. logging.warning('url error %s', e.reason)
  196. except socket.timeout as e:
  197. logging.warning('socket timeout')
  198. def deleteActor(self, actor_id):
  199. url = self.__getApiUrl('actors/{}'.format(actor_id))
  200. logging.debug('Calling %s', url)
  201. req = urllib2.Request(url=url)
  202. req.get_method = lambda: 'DELETE'
  203. try:
  204. response = urllib2.urlopen(req, timeout=self.timeout).read()
  205. logging.info("Deleting actor %s", response)
  206. return json.loads(response)
  207. except HTTPError as e:
  208. logging.warning('http error %s', e.code)
  209. except URLError as e:
  210. logging.warning('url error %s', e.reason)
  211. except socket.timeout as e:
  212. logging.warning('socket timeout')
  213. def sendData(self, datatype, data):
  214. """
  215. Sends additional data which should not be processed via the unitstate call (e.g. images)
  216. """
  217. warnings.warn("Sending additional data packages is currently not implemented")