RainEagle library plus script for polling data
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

869 lines
28 KiB

  1. __author__ = 'Peter Shipley <peter.shipley@gmail.com>'
  2. __copyright__ = "Copyright (C) 2014 Peter Shipley"
  3. __license__ = "BSD"
  4. __version__ = "0.1.7"
  5. import socket
  6. import sys
  7. import os
  8. import time
  9. import xml.etree.ElementTree as ET
  10. import urllib
  11. import urllib2
  12. from math import floor
  13. from urlparse import urlparse
  14. import json
  15. from warnings import warn
  16. from distutils.version import LooseVersion
  17. min_fw_ver = "2.0.21"
  18. from pprint import pprint
  19. # api_arg_format = { }
  20. __all__ = ['Eagle', 'RainEagleResponseError', 'to_epoch_1970, to_epoch_2000']
  21. class RainEagleResponseError(RuntimeError):
  22. """General exception for responce errors
  23. from Rainforest Automation EAGLE (RFA-Z109)
  24. """
  25. pass
  26. def to_epoch_2000(t) :
  27. """ converts time stored as
  28. to unix's epoch of 1970
  29. offset in seconds from "Jan 1 00:00:00 2000"
  30. """
  31. if isinstance(t, time.struct_time) :
  32. t = time.mktime(t)
  33. return t - 946684800
  34. def to_epoch_1970(t) :
  35. """ converts time stored as
  36. offset in seconds from "Jan 1 00:00:00 2000"
  37. to unix's epoch of 1970
  38. """
  39. if isinstance(t, (int, long, float)) :
  40. return t + 946684800
  41. if isinstance(t, str) and t.startswith('0x') :
  42. return 946684800 + int(t, 16)
  43. def _et2d(et) :
  44. """ Etree to Dict
  45. converts an ETree to a Dict Tree
  46. lists are created for duplicate tag
  47. if there are multiple XML of the same name
  48. an list array is used
  49. attrib tags are converted to "tag_name" + "attrib_name"
  50. if an invalid arg is passed a empty dict is retrurned
  51. arg: ETree Element obj
  52. returns: a dict obj
  53. """
  54. d = dict()
  55. if not isinstance(et, ET.Element) :
  56. return d
  57. children = list(et)
  58. if et.attrib :
  59. for k, v in list(et.items()) :
  60. d[et.tag + "-" + k] = v
  61. if children :
  62. for child in children :
  63. if child.tag in d :
  64. if type(d[child.tag]) != list :
  65. t = d[child.tag]
  66. d[child.tag] = [t]
  67. if list(child) or child.attrib :
  68. if child.tag in d :
  69. d[child.tag].append(_et2d(child))
  70. else :
  71. d[child.tag] = _et2d(child)
  72. else :
  73. if child.tag in d :
  74. d[child.tag].append(child.text)
  75. else :
  76. d[child.tag] = child.text
  77. return d
  78. def _twos_comp(val, bits=32):
  79. """compute the 2's compliment of int value val"""
  80. if( (val&(1<<(bits-1))) != 0 ):
  81. val = val - (1<<bits)
  82. return val
  83. def _tohex(n, width=8) :
  84. """
  85. convert arg to string with hex representation if possible
  86. use twos-complement for negitive 32bit numbers
  87. use int class to convert whatever is handed to us
  88. """
  89. if isinstance(n, str) and n.startswith('0x') :
  90. return(n)
  91. i = int(n)
  92. # add two for the "0x"
  93. width += 2
  94. if (i > 2147483647) or (i < -2147483648) :
  95. warn("_tohex : signed int to large (" + str(n) + ")\n",
  96. RuntimeWarning, stacklevel=2)
  97. if i < 0 :
  98. i += 0x100000000
  99. return "{:#0{width}x}".format(int(i), width=width)
  100. #
  101. class Eagle(object) :
  102. """
  103. Class for talking to Rainforest Automation EAGLE (RFA-Z109)
  104. args:
  105. debug print debug messages if true
  106. addr address of device
  107. port port on device (default 5002)
  108. getmac connect to device at start up and get macid (default true)
  109. timeout TCP socket timeout
  110. Currently there is very little error handling ( if any at all )
  111. """
  112. def __init__(self, **kwargs):
  113. self.debug = kwargs.get("debug", 0)
  114. if self.debug :
  115. print self.__class__.__name__, __name__
  116. self.checkfw = kwargs.get("checkfirmware", True)
  117. self.addr = kwargs.get("addr", os.getenv('EAGLE_ADDR', None))
  118. self.port = kwargs.get("port", os.getenv('EAGLE_PORT', 5002))
  119. self.mac = kwargs.get("mac", None)
  120. self.timeout = kwargs.get("timeout", 10)
  121. self.soc = None
  122. self.macid = None
  123. if self.debug :
  124. print "Addr : = ", self.addr
  125. print "timeout : = ", self.timeout
  126. print "debug : = ", self.debug
  127. if self.addr is None :
  128. raise AssertionError("no hostname or IP given")
  129. # preload
  130. if self.mac is None :
  131. self.device_info = self.list_devices()
  132. if self.device_info is None :
  133. raise IOError("Error connecting")
  134. if self.debug :
  135. print "__init__ ",
  136. pprint(self.device_info)
  137. # self.macid = self.device_info['DeviceInfo']['DeviceMacId']
  138. if self.debug :
  139. print "Init DeviceMacId = ", self.macid
  140. if self.checkfw :
  141. mysetting = self.get_setting_data()
  142. dev_fw_ver = mysetting['device_fw_version']
  143. if ( LooseVersion(dev_fw_ver) < LooseVersion(min_fw_ver) ) :
  144. warn_message = "Warning : device firmware " \
  145. + "{0} < {1} please concideer " \
  146. + "updating ".format(dev_fw_ver, min_fw_ver)
  147. warn( warn_message, RuntimeWarning, stacklevel=3)
  148. # socket commands as class functions
  149. def list_devices(self, macid=None):
  150. """
  151. Send the LIST_DEVICES command
  152. returns information about the EAGLE device
  153. """
  154. comm_responce = self._send_soc_comm("list_devices", MacId=macid)
  155. if self.debug :
  156. print "comm_responce =", comm_responce
  157. if comm_responce is None:
  158. raise RainEagleResponseError("list_devices : Null reply")
  159. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  160. rv = _et2d(etree)
  161. if self.macid is None :
  162. self.macid = rv['DeviceInfo']['DeviceMacId']
  163. return rv
  164. # 3
  165. def get_device_data(self, macid=None) :
  166. """ Send the GET_DEVICE_DATA command to get a data dump """
  167. if macid is None :
  168. macid = self.macid
  169. comm_responce = self._send_soc_comm("get_device_data", MacId=macid)
  170. if comm_responce is None:
  171. raise RainEagleResponseError("get_device_data : Null reply")
  172. if self.debug :
  173. print comm_responce
  174. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  175. rv = _et2d(etree)
  176. return rv
  177. # 10
  178. def get_instantaneous_demand(self, macid=None) :
  179. """ Send the GET_INSTANTANEOUS_DEMAND command
  180. get the real time demand from the meter
  181. args:
  182. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  183. """
  184. if macid is None :
  185. macid = self.macid
  186. comm_responce = self._send_soc_comm("get_instantaneous_demand",
  187. MacId=macid)
  188. if comm_responce is None:
  189. raise RainEagleResponseError("get_instantaneous_demand : Null reply")
  190. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  191. rv = _et2d(etree)
  192. return rv
  193. # 11
  194. def get_demand_values(self, macid=None, interval="hour", frequency=None) :
  195. """ Send the GET_DEMAND_VALUES command
  196. get a series of instantaneous demand values
  197. args:
  198. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  199. Interval hour | day | week
  200. [Frequency] int seconds between samples
  201. """
  202. if macid is None :
  203. macid = self.macid
  204. if interval not in ['hour', 'day', 'week' ] :
  205. raise ValueError("get_demand_values interval must be 'hour', 'day' or 'week' ")
  206. kwargs = {"MacId": macid, "Interval": interval}
  207. if frequency :
  208. kwargs["Frequency"] = str(frequency)
  209. comm_responce = self._send_soc_comm("get_demand_values", **kwargs)
  210. if comm_responce is None:
  211. raise RainEagleResponseError("get_demand_values : Null reply")
  212. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  213. rv = _et2d(etree)
  214. return rv
  215. # 12
  216. def get_summation_values(self, macid=None, interval="day") :
  217. """ Send the GET_SUMMATION_VALUES command
  218. get a series of net summation values
  219. args:
  220. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  221. Interval day | week | month | year
  222. """
  223. if macid is None :
  224. macid = self.macid
  225. if interval not in ['day', 'week', 'month', 'year'] :
  226. raise ValueError("get_summation_values interval must be 'day', 'week', 'month' or 'year'")
  227. comm_responce = self._send_soc_comm("get_summation_values",
  228. MacId=macid, Interval=interval)
  229. if comm_responce is None:
  230. raise RainEagleResponseError("get_summation_values : Null reply")
  231. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  232. rv = _et2d(etree)
  233. return rv
  234. # 14
  235. def set_fast_poll(self, macid=None, frequency="0x04", duration="0xFF") :
  236. """ Send the SET_FAST_POLL command
  237. set the fast poll mode on the meter
  238. args:
  239. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  240. Frequency 0x01 - 0xFF Freq to poll meter, in seconds
  241. Duration 0x00 - 0x0F Duration of fast poll mode, in minutes (max 15)
  242. """
  243. if macid is None :
  244. macid = self.macid
  245. frequency = _tohex(frequency, 2)
  246. duration = _tohex(duration, 2)
  247. comm_responce = self._send_soc_comm("get_instantaneous_demand",
  248. MacId=macid, Frequency=frequency, Duration=duration)
  249. if comm_responce is None:
  250. raise RainEagleResponseError("set_fast_poll : Null reply")
  251. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  252. rv = _et2d(etree)
  253. return rv
  254. # 15
  255. def get_fast_poll_status(self, macid=None) :
  256. """ Send the GET_FAST_POLL_STATUS command
  257. get the current status of fast poll mode.
  258. args:
  259. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  260. """
  261. if macid is None :
  262. macid = self.macid
  263. comm_responce = self._send_soc_comm("get_fast_poll_status", MacId=macid)
  264. if comm_responce is None:
  265. return None
  266. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  267. rv = _et2d(etree)
  268. return rv
  269. # 17
  270. # needs to be rewritten to stream the data via iter
  271. def get_history_data(self, macid=None, starttime="0x00000000", endtime=None, frequency=None) :
  272. """ Send the GET_HISTORY_DATA command
  273. get a series of summation values over an interval of time
  274. ( socket command api )
  275. args:
  276. MacId 16 hex digits, MAC addr of EAGLE ZigBee radio
  277. StartTime the start of the history interval (default oldest sample)
  278. EndTime the end of the history interval (default current time)
  279. Frequency Requested number of seconds between samples.
  280. """
  281. if macid is None :
  282. macid = self.macid
  283. kwargs = {"MacId": macid}
  284. kwargs["StartTime"] = _tohex(starttime, 8)
  285. if endtime :
  286. kwargs["EndTime"] = _tohex(endtime, 8)
  287. if frequency :
  288. kwargs["Frequency"] = _tohex(endtime, 4)
  289. comm_responce = self._send_soc_comm("get_history_data", **kwargs)
  290. if comm_responce is None :
  291. raise RainEagleResponseError("get_history_data : Null reply")
  292. etree = ET.fromstring('<S>' + comm_responce + '</S>')
  293. rv = _et2d(etree)
  294. return rv
  295. # http commands as class functions
  296. def get_device_list(self, macid=None) :
  297. """
  298. Send the LIST_DEVICES command
  299. returns information about the EAGLE device
  300. """
  301. comm_responce = self._send_http_comm("get_device_list", MacId=macid)
  302. return json.loads(comm_responce)
  303. def get_uploaders(self, macid=None) :
  304. """
  305. gets list of uploaders for Web UI
  306. On Success returns dict with the values (example):
  307. 'uploader[0]': 'none'
  308. 'uploader[1]': 'bidgely'
  309. 'uploader_name[0]': 'None'
  310. 'uploader_name[1]': 'Bidgely Inc.'
  311. """
  312. comm_responce = self._send_http_comm("get_uploaders", MacId=macid)
  313. return json.loads(comm_responce)
  314. def get_uploader(self, macid=None) :
  315. """
  316. gets current uploaders config
  317. On Success returns dict with the values (example):
  318. "uploader_timestamp" : "1394503703"
  319. "uploader_provider" : "bidgely"
  320. "uploader_protocol" : "https"
  321. "uploader_hostname" : "api.bidgely.com"
  322. "uploader_url" : "/v1/users/44441b47-1b9a-4a65-8e8c-0efefe05bb88/homes/1/gateways/1"
  323. "uploader_port" : "0"
  324. "uploader_auth_code" : "44441b47-1b9a-4a65-8e8c-0efefe05bb88"
  325. "uploader_email" : ""
  326. "uploader_user_id" : ""
  327. "uploader_password" : ""
  328. "uploader_enabled" : "Y"
  329. See also set_cloud() to set current uploader cloud config
  330. """
  331. comm_responce = self._send_http_comm("get_uploader", MacId=macid)
  332. return json.loads(comm_responce)
  333. def set_message_read(self, macid=None) :
  334. """
  335. On Success returns dict with the values :
  336. 'remote_management_status' : 'success'
  337. """
  338. comm_responce = self._send_http_comm("set_message_read", MacId=macid)
  339. return json.loads(comm_responce)
  340. def confirm_message(self, macid=None, id=None) :
  341. """
  342. """
  343. id = _tohex(id)
  344. comm_responce = self._send_http_comm("confirm_message",
  345. MacId=macid, Id=id)
  346. return json.loads(comm_responce)
  347. def get_message(self, macid=None) :
  348. """
  349. On Success returns dict with the values (example):
  350. "meter_status" : "Connected"
  351. "message_timestamp" : "946684800"
  352. "message_text" : ""
  353. "message_confirmed" : "N"
  354. "message_confirm_required" : "N"
  355. "message_id" : "0"
  356. "message_queue" : "active"
  357. "message_priority" : ""
  358. "message_read" : "Y"
  359. """
  360. comm_responce = self._send_http_comm("get_message", MacId=macid)
  361. return json.loads(comm_responce)
  362. def get_usage_data(self, macid=None) :
  363. """
  364. Get current demand usage summation
  365. On Success returns dict with the values (example):
  366. 'demand' : '0.4980'
  367. 'demand_timestamp' : '1394505386'
  368. 'demand_units' : 'kW'
  369. 'message_confirm_required' : 'N'
  370. 'message_confirmed' : 'N'
  371. 'message_id' : '0'
  372. 'message_priority' : ''
  373. 'message_queue' : active'
  374. 'message_read' : 'Y'
  375. 'message_text' : ''
  376. 'message_timestamp' : '946684800'
  377. 'meter_status' : 'Connected'
  378. 'price' : '0.1400'
  379. 'price_label' : 'Set by User'
  380. 'price_units' : '$'
  381. 'summation_delivered' : '2667.867'
  382. 'summation_received' : '37.283'
  383. 'summation_units' : 'kWh'
  384. 'usage_timestamp' : '1394505386'
  385. """
  386. comm_responce = self._send_http_comm("get_usage_data", MacId=macid)
  387. return json.loads(comm_responce)
  388. def get_historical_data(self, macid=None, period="day") :
  389. """
  390. get a series of summation values over an interval of time
  391. ( http command api )
  392. args:
  393. period day|week|month|year
  394. On Success returns dict with the values (example):
  395. 'data_period' 'day'
  396. 'data_size' '14'
  397. 'timestamp[0]' '1394422200'
  398. 'timestamp[1]' '1394425800'
  399. 'timestamp[2]' '1394429400'
  400. 'timestamp[3]' '1394433000'
  401. 'timestamp[4]' '1394436600'
  402. 'timestamp[5]' '1394440200'
  403. 'timestamp[6]' '1394443800'
  404. 'timestamp[7]' '1394447400'
  405. 'timestamp[8]' '1394451000'
  406. 'timestamp[9]' '1394454600'
  407. 'timestamp[10]' '1394458200'
  408. 'timestamp[11]' '1394461800'
  409. 'timestamp[12]' '1394465400'
  410. 'timestamp[13]' '1394469000'
  411. 'value[0]' '0.429'
  412. 'value[1]' '0.426'
  413. 'value[2]' '0.422'
  414. 'value[3]' '0.627'
  415. 'value[4]' '0.735'
  416. 'value[5]' '0.193'
  417. 'value[6]' '0.026'
  418. 'value[7]' '-0.985'
  419. 'value[8]' '-1.491'
  420. 'value[9]' '-2.196'
  421. 'value[11]' '-1.868'
  422. 'value[12]' '-1.330'
  423. 'value[13]' '-0.870'
  424. """
  425. if period not in ['day', 'week', 'month', 'year'] :
  426. raise ValueError("get_historical_data : period must be one of day|week|month|year")
  427. comm_responce = self._send_http_comm("get_historical_data", macid=macid, Period=period)
  428. return json.loads(comm_responce)
  429. def get_setting_data(self, macid=None) :
  430. """
  431. get settings data
  432. On Success returns dict with value containing setting
  433. relating to price, uploader, network & device
  434. """
  435. comm_responce = self._send_http_comm("get_setting_data", MacId=macid)
  436. return json.loads(comm_responce)
  437. def get_device_config(self, macid=None) :
  438. """
  439. get remote management status
  440. On Success returns dict with value 'Y' or 'N' :
  441. 'config_ssh_enabled': 'Y'
  442. 'config_vpn_enabled': 'Y'
  443. """
  444. comm_responce = self._send_http_comm("get_device_config", MacId=macid)
  445. return json.loads(comm_responce)
  446. def get_gateway_info(self, macid=None) :
  447. """
  448. gets network status
  449. On Success returns dict with the values (example):
  450. 'gateway_cloud_id': '00:09:69'
  451. 'gateway_internet_status': 'connected'
  452. 'gateway_ip_addr': '10.11.12.13'
  453. 'gateway_mac_id': 'D8:D5:B9:00:90:24'
  454. """
  455. comm_responce = self._send_http_comm("get_gateway_info", MacId=macid)
  456. return json.loads(comm_responce)
  457. def get_timezone(self, macid=None) :
  458. """
  459. get current timezone configuration
  460. On Success returns dict with the value :
  461. 'timezone_localTime': '1394527011'
  462. 'timezone_olsonName': 'UTC/GMT'
  463. 'timezone_status': '2'
  464. 'timezone_utcOffset': 'UTC'
  465. 'timezone_utcTime': '1394527011'
  466. 'timezone_status': 'success'
  467. """
  468. comm_responce = self._send_http_comm("get_timezone", MacId=macid)
  469. return json.loads(comm_responce)
  470. def get_time_source(self, macid=None) :
  471. """
  472. get time source for device
  473. On Success returns dict with value 'internet' or 'meter' :
  474. 'time_source': 'internet'
  475. """
  476. comm_responce = self._send_http_comm("get_time_source", MacId=macid)
  477. return json.loads(comm_responce)
  478. def get_remote_management(self, macid=None) :
  479. return self.get_device_config(self, MacId=macid)
  480. def set_remote_management(self, macid=None, status="on") :
  481. """ set_remote_management
  482. enabling ssh & vpn
  483. args:
  484. status on|off
  485. On Success returns dict with value :
  486. 'remote_management_status': 'success'
  487. """
  488. if status not in ['on', 'off'] :
  489. raise ValueError("set_remote_management status must be 'on' or 'off'")
  490. comm_responce = self._send_http_comm("set_remote_management",
  491. MacId=macid, Status=status)
  492. return json.loads(comm_responce)
  493. def set_time_source(self, macid=None, source=None) :
  494. """ set_time_source
  495. set time source
  496. args:
  497. source meter|internet
  498. On Success returns dict with value :
  499. 'set_time_source_status': u'success'
  500. On Error returns dict with value :
  501. 'set_time_source_status': 'invalid source name'
  502. """
  503. if source not in ['meter', 'internet'] :
  504. raise ValueError("set_time_source Source must be 'meter' or 'internet'")
  505. comm_responce = self._send_http_comm("set_time_source",
  506. MacId=macid, Source=source)
  507. return json.loads(comm_responce)
  508. def get_price(self, macid=None) :
  509. """
  510. get price for kWh
  511. On Success returns (example):
  512. price': '0.1300'
  513. price_label': 'Set by User' or '--'
  514. price_timestamp': '1394524458'
  515. price_units': '$'
  516. returns empty dict on Error
  517. """
  518. comm_responce = self._send_http_comm("get_price", MacId=macid)
  519. return json.loads(comm_responce)
  520. def set_price(self, macid=None, price=None) :
  521. """
  522. Set price manualy
  523. args:
  524. price Price/kWh
  525. On Success returns dict with value :
  526. 'set_price_status': 'success'
  527. """
  528. if isinstance(price, str) and price.startswith('$') :
  529. price = float(price.lstrip('$'))
  530. if not isinstance(price, (int, long, float)) :
  531. raise ValueError("set_price price arg must me a int, long or float")
  532. if (price <= 0):
  533. raise ValueError("set_price price arg greater then 0")
  534. trailing_digits = 0
  535. multiplier = 1
  536. while (((price * multiplier) != (floor(price * multiplier))) and (trailing_digits < 7)) :
  537. trailing_digits += 1
  538. multiplier *= 10
  539. price_adj = "{:#x}".format(int(price * multiplier))
  540. tdigits = "{:#x}".format(trailing_digits)
  541. comm_responce = self._send_http_comm("set_price", MacId=macid,
  542. Price=price_adj, TrailingDigits=tdigits)
  543. return json.loads(comm_responce)
  544. def set_price_auto(self, macid=None) :
  545. """
  546. Set Price from Meter
  547. On Success returns dict with value :
  548. 'set_price_status': 'success'
  549. """
  550. comm_responce = self._send_http_comm("set_price",
  551. MacId=macid,
  552. Price="0xFFFFFFFF",
  553. TrailingDigits="0x00")
  554. return json.loads(comm_responce)
  555. # def set_multiplier_divisor(self, multiplier=1, divisor=1) :
  556. # """
  557. # set multiplier and divisor manualy
  558. # """
  559. # multiplier = _tohex(multiplier, 8)
  560. # divisor = _tohex(divisor, 8)
  561. # comm_responce = self._send_http_comm("set_multiplier_divisor", MacId=macid, Multiplier=multiplier, Divisor=divisor)
  562. # return json.loads(comm_responce)
  563. def factory_reset(self, macid=None) :
  564. """
  565. Factory Reset
  566. """
  567. comm_responce = self._send_http_comm("factory_reset", MacId=macid)
  568. return json.loads(comm_responce)
  569. # def disconnect_meter(self, macid=None) :
  570. # """
  571. # disconnect from Smart Meter
  572. # """
  573. # comm_responce = self._send_http_comm("disconnect_meter", MacId=macid)
  574. # return json.loads(comm_responce)
  575. def cloud_reset(self, macid=None) :
  576. """
  577. cloud_reset : Clear Cloud Configuration
  578. """
  579. comm_responce = self._send_http_comm("cloud_reset", MacId=macid)
  580. return json.loads(comm_responce)
  581. def set_cloud(self, macid=None, url=None, authcode="", email="") :
  582. """
  583. set cloud Url
  584. args:
  585. url Url for uploader
  586. authcode
  587. email
  588. See also get_uploader() to retrieve current uploader cloud config
  589. """
  590. if url is None :
  591. raise ValueError("invalid url.\n")
  592. if url.__len__() > 200 :
  593. raise ValueError("Max URL length is 200 characters long.\n")
  594. urlp = urlparse(url)
  595. if urlp.port :
  596. port = "{:#04x}".format(urlp.port)
  597. else :
  598. port = "0x00"
  599. hostname = urlp.hostname
  600. if urlp.scheme :
  601. protocol = urlp.scheme
  602. else :
  603. protocol = "http"
  604. url = urlp.path
  605. if urlp.username :
  606. userid = urlp.username
  607. else :
  608. userid = ""
  609. if urlp.password :
  610. password = urlp.password
  611. else :
  612. password = ""
  613. comm_responce = self._send_http_comm("set_cloud", MacId=macid,
  614. Provider="manual",
  615. Protocol=protocol, HostName=hostname,
  616. Url=url, Port=port,
  617. AuthCode=authcode, Email=email,
  618. UserId=userid, Password=password)
  619. return json.loads(comm_responce)
  620. # Support functions
  621. def _connect(self) :
  622. self.soc = socket.create_connection(
  623. (self.addr, self.port), self.timeout)
  624. def _disconnect(self):
  625. try :
  626. if self.soc :
  627. self.soc.close()
  628. self.soc = False
  629. except IOError :
  630. pass
  631. def _send_http_comm(self, cmd, **kwargs):
  632. if self.debug :
  633. print "\n\n_send_http_comm : ", cmd
  634. commstr = "<LocalCommand>\n"
  635. commstr += "<Name>{0!s}</Name>\n".format(cmd)
  636. commstr += "<MacId>{0!s}</MacId>\n".format(self.macid)
  637. for k, v in kwargs.items() :
  638. commstr += "<{0}>{1!s}</{0}>\n".format(k, v)
  639. commstr += "</LocalCommand>\n"
  640. if cmd == "set_cloud" :
  641. print(commstr)
  642. return dict()
  643. if self.debug :
  644. print(commstr)
  645. url = "http://{0}/cgi-bin/cgi_manager".format(self.addr)
  646. req = urllib2.Request(url, commstr)
  647. response = urllib2.urlopen(req)
  648. the_page = response.read()
  649. return the_page
  650. def _send_soc_comm(self, cmd, **kwargs):
  651. if cmd == "set_fast_poll" :
  652. command_tag = "RavenCommand"
  653. else :
  654. command_tag = "LocalCommand"
  655. commstr = "<{0}>\n ".format(command_tag)
  656. commstr += "<Name>{0!s}</Name>\n".format(cmd)
  657. for k, v in kwargs.items() :
  658. commstr += "<{0}>{1!s}</{0}>\n".format(k, v)
  659. commstr += "</{0}>\n".format(command_tag)
  660. replystr = ""
  661. # buf_list = []
  662. try:
  663. self._connect()
  664. # if cmd == "get_history_data" :
  665. # self.soc.settimeout(45)
  666. self.soc.sendall(commstr)
  667. if self.debug :
  668. print "commstr : \n", commstr
  669. # time.sleep(1)
  670. while 1 :
  671. buf = self.soc.recv(1000)
  672. if not buf:
  673. break
  674. replystr += buf
  675. #buf_list.append(buf)
  676. # replystr = ''.join(buf_list)
  677. except Exception:
  678. print("Unexpected error:", sys.exc_info()[0])
  679. print "Error replystr = ", replystr
  680. replystr = None
  681. finally:
  682. self._disconnect()
  683. if self.debug > 1 :
  684. print "_send_soc_comm replystr :\n", replystr
  685. return replystr
  686. # Do nothing
  687. # (syntax check)
  688. #
  689. if __name__ == "__main__":
  690. import __main__
  691. print(__main__.__file__)
  692. print("syntax ok")
  693. exit(0)