decode-status.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/usr/bin/env python
  2. """
  3. decode-status.py - decode status for Sonoff-Tasmota
  4. Copyright (C) 2018 Theo Arends
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. Requirements:
  16. - Python
  17. - pip json pycurl
  18. Instructions:
  19. Execute command with option -d to retrieve status report from device or
  20. get a copy of the status message with http command http://sonoff/cm?cmnd=status%200
  21. and store it in file status.json
  22. Usage:
  23. ./decode-status.py -d <hostname or IP address> [-u username] [-p password]
  24. or
  25. ./decode-status.py -f <JSON status information file>
  26. Example:
  27. ./decode-status.py -d sonoff1
  28. ./decode-status.py -d sonoff1 -p 12345678
  29. or
  30. ./decode-status.py -f status.json
  31. """
  32. import io
  33. import os.path
  34. import json
  35. import pycurl
  36. import urllib2
  37. from sys import exit
  38. from optparse import OptionParser
  39. from StringIO import StringIO
  40. a_on_off = ["OFF","ON "]
  41. a_setoption = [[
  42. "Save power state and use after restart",
  43. "Restrict button actions to single, double and hold",
  44. "Show value units in JSON messages",
  45. "MQTT enabled",
  46. "Respond as Command topic instead of RESULT",
  47. "MQTT retain on Power",
  48. "MQTT retain on Button",
  49. "MQTT retain on Switch",
  50. "Convert temperature to Fahrenheit",
  51. "MQTT retain on Sensor",
  52. "MQTT retained LWT to OFFLINE when topic changes",
  53. "Swap Single and Double press Button",
  54. "Do not use flash page rotate",
  55. "Button single press only",
  56. "Power interlock mode",
  57. "Do not allow PWM control",
  58. "Reverse clock",
  59. "Allow entry of decimal color values",
  60. "CO2 color to light signal",
  61. "HASS discovery",
  62. "Do not control Power with Dimmer",
  63. "Energy monitoring while powered off",
  64. "MQTT serial",
  65. "MQTT serial binary",
  66. "Convert pressure to mmHg",
  67. "KNX enabled",
  68. "Use Power device index on single relay devices",
  69. "KNX enhancement",
  70. "RF receive decimal",
  71. "IR receive decimal",
  72. "Enforce HASS light group",
  73. "Do not show Wifi and Mqtt state using Led"
  74. ],[
  75. "Timers enabled",
  76. "Generic ESP8285 GPIO enabled",
  77. "Add UTC time offset to JSON message",
  78. "Show hostname and IP address in GUI",
  79. "Apply SetOption20 to Tuya",
  80. "Use short Hass discovery messages",
  81. "Use wifi network scan at restart",
  82. "Use wifi network rescan regularly",
  83. "Add IR raw data to JSON message",
  84. "Change state topic from tele/STATE to stat/RESULT",
  85. "Enable normal sleep instead of dynamic sleep",
  86. "",
  87. "","","","",
  88. "","","","",
  89. "","","","",
  90. "","","","",
  91. "","","",""
  92. ]]
  93. a_features = [[
  94. "","","USE_I2C","USE_SPI",
  95. "USE_DISCOVERY","USE_ARDUINO_OTA","USE_MQTT_TLS","USE_WEBSERVER",
  96. "WEBSERVER_ADVERTISE","USE_EMULATION","MQTT_PUBSUBCLIENT","MQTT_TASMOTAMQTT",
  97. "MQTT_ESPMQTTARDUINO","MQTT_HOST_DISCOVERY","USE_ARILUX_RF","USE_WS2812",
  98. "USE_WS2812_DMA","USE_IR_REMOTE","USE_IR_HVAC","USE_IR_RECEIVE",
  99. "USE_DOMOTICZ","USE_DISPLAY","USE_HOME_ASSISTANT","USE_SERIAL_BRIDGE",
  100. "USE_TIMERS","USE_SUNRISE","USE_TIMERS_WEB","USE_RULES",
  101. "USE_KNX","USE_WPS","USE_SMARTCONFIG","MQTT_ARDUINOMQTT"
  102. ],[
  103. "USE_CONFIG_OVERRIDE","BE_MINIMAL","USE_SENSORS","USE_CLASSIC",
  104. "USE_KNX_NO_EMULATION","USE_DISPLAY_MODES1TO5","USE_DISPLAY_GRAPH","USE_DISPLAY_LCD",
  105. "USE_DISPLAY_SSD1306","USE_DISPLAY_MATRIX","USE_DISPLAY_ILI9341","USE_DISPLAY_EPAPER",
  106. "USE_DISPLAY_SH1106","USE_MP3_PLAYER","USE_PCA9685","USE_TUYA_DIMMER",
  107. "USE_RC_SWITCH","USE_ARMTRONIX_DIMMERS","","",
  108. "","","","NO_EXTRA_4K_HEAP",
  109. "VTABLES_IN_IRAM","VTABLES_IN_DRAM","VTABLES_IN_FLASH","PIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH",
  110. "PIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY","PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH","DEBUG_THEO","USE_DEBUG_DRIVER"
  111. ],[
  112. "","USE_ADC_VCC","USE_ENERGY_SENSOR","USE_PZEM004T",
  113. "USE_DS18B20","USE_DS18x20_LEGACY","USE_DS18x20","USE_DHT",
  114. "USE_SHT","USE_HTU","USE_BMP","USE_BME680",
  115. "USE_BH1750","USE_VEML6070","USE_ADS1115_I2CDEV","USE_ADS1115",
  116. "USE_INA219","USE_SHT3X","USE_MHZ19","USE_TSL2561",
  117. "USE_SENSEAIR","USE_PMS5003","USE_MGS","USE_NOVA_SDS",
  118. "USE_SGP30","USE_SR04","USE_SDM120","USE_SI1145",
  119. "USE_SDM630","USE_LM75AD","USE_APDS9960","USE_TM1638"
  120. ],[
  121. "USE_MCP230xx","USE_MPR121","USE_CCS811","USE_MPU6050",
  122. "USE_MCP230xx_OUTPUT","USE_MCP230xx_DISPLAYOUTPUT","USE_HLW8012","USE_CSE7766",
  123. "USE_MCP39F501","USE_PZEM_AC","USE_DS3231","USE_HX711",
  124. "USE_PZEM_DC","USE_TX20_WIND_SENSOR","USE_MGC3130","USE_RF_SENSOR",
  125. "USE_THEO_V2","USE_ALECTO_V2","","",
  126. "","","","",
  127. "","","","",
  128. "","","",""]]
  129. usage = "usage: decode-status {-d | -f} arg"
  130. parser = OptionParser(usage)
  131. parser.add_option("-d", "--dev", action="store", type="string",
  132. dest="device", help="device to retrieve status from")
  133. parser.add_option("-u", "--username", action="store", type="string",
  134. dest="username", help="username for login", default="admin")
  135. parser.add_option("-p", "--password", action="store", type="string",
  136. dest="password", help="password for login", default=None)
  137. parser.add_option("-f", "--file", metavar="FILE",
  138. dest="jsonfile", default="status.json", help="status json file (default: status.json)")
  139. (options, args) = parser.parse_args()
  140. if (options.device):
  141. buffer = StringIO()
  142. loginstr = ""
  143. if options.password is not None:
  144. loginstr = "user={}&password={}&".format(urllib2.quote(options.username), urllib2.quote(options.password))
  145. url = str("http://{}/cm?{}cmnd=status%200".format(options.device, loginstr))
  146. c = pycurl.Curl()
  147. c.setopt(c.URL, url)
  148. c.setopt(c.WRITEDATA, buffer)
  149. c.perform()
  150. c.close()
  151. body = buffer.getvalue()
  152. obj = json.loads(body)
  153. else:
  154. jsonfile = options.jsonfile
  155. fp = open(jsonfile, "r")
  156. obj = json.load(fp)
  157. fp.close()
  158. def StartDecode():
  159. print ("\n*** decode-status.py v20180730 by Theo Arends ***")
  160. # print("Decoding\n{}".format(obj))
  161. if ("StatusSNS" in obj):
  162. if ("Time" in obj["StatusSNS"]):
  163. time = str(" from status report taken at {}".format(obj["StatusSNS"]["Time"]))
  164. if ("Status" in obj):
  165. if ("FriendlyName" in obj["Status"]):
  166. print("Decoding information for device {}{}".format(obj["Status"]["FriendlyName"][0], time))
  167. if ("StatusLOG" in obj):
  168. if ("SetOption" in obj["StatusLOG"]):
  169. options = []
  170. o = 0
  171. p = 0
  172. r = 1
  173. if (len(obj["StatusLOG"]["SetOption"]) == 3):
  174. r = 2
  175. for f in range(r):
  176. if (f == 1):
  177. o = 2
  178. p = 50
  179. option = obj["StatusLOG"]["SetOption"][o]
  180. i_option = int(option,16)
  181. for i in range(len(a_setoption[f])):
  182. if (a_setoption[f][i]):
  183. state = (i_option >> i) & 1
  184. options.append(str("{0:2d} ({1}) {2}".format(i + p, a_on_off[state], a_setoption[f][i])))
  185. print("\nOptions")
  186. for i in range(len(options)):
  187. print(" {}".format(options[i]))
  188. if ("StatusMEM" in obj):
  189. if ("Features" in obj["StatusMEM"]):
  190. features = []
  191. for f in range(5):
  192. feature = obj["StatusMEM"]["Features"][f]
  193. i_feature = int(feature,16)
  194. if (f == 0):
  195. features.append(str("Language LCID = {}".format(i_feature & 0xFFFF)))
  196. else:
  197. for i in range(len(a_features[f -1])):
  198. if ((i_feature >> i) & 1):
  199. features.append(a_features[f -1][i])
  200. features.sort()
  201. print("\nFeatures")
  202. for i in range(len(features)):
  203. print(" {}".format(features[i]))
  204. if __name__ == "__main__":
  205. try:
  206. StartDecode()
  207. except Exception as e:
  208. print("E: {}".format(e))