{{ ybox2 - X10 Bridge http://www.deepdarc.com/ybox2 ABOUT This is a simple HTTP interface to X10. }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 MAX_DEVICES = 10 ' maximum number of 1-wire devices OW_PIN = 30 X10_ZC_PIN = 26 X10_IN_PIN = 25 X10_OUT_PIN = 24 OBJ term : "TV_Text" subsys : "subsys" settings : "settings" numbers : "numbers" socket : "api_telnet_serial" http : "http" base16 : "base16" auth : "auth_digest" X10 : "X10" fp : "FloatString" f : "FloatMath" ' could also use Float32 VAR long stack[100] byte stage_two byte tv_mode long hits DAT productName BYTE "X10 Bridge",0 productURL BYTE "http://www.deepdarc.com/ybox2/",0 PUB init | i ' outa[0]:=0 ' dira[0]:=1 dira[subsys#SPKRPin]:=1 ' Default to NTSC tv_mode:=term#MODE_NTSC hits:=0 settings.start numbers.init settings.removeKey(settings#MISC_STAGE2) subsys.init ' If there is a TV mode preference in the EEPROM, load it up. if settings.findKey(settings#MISC_TV_MODE) tv_mode := settings.getByte(settings#MISC_TV_MODE) ' Start the TV Terminal term.startWithMode(12,tv_mode) term.str(string($0C,7)) term.str(@productName) term.out(13) term.str(@productURL) term.out(13) term.out($0c) term.out(2) repeat term#cols/2 term.out($8E) term.out($88) term.out($0c) term.out(0) subsys.StatusLoading if settings.getData(settings#NET_MAC_ADDR,@stack,6) term.str(string("MAC: ")) repeat i from 0 to 5 if i term.out("-") term.hex(byte[@stack][i],2) term.out(13) dira[subsys#SPKRPin]:=!settings.findKey(settings#MISC_SOUND_DISABLE) dira[0]:=0 if not \socket.start(1,2,3,4,6,7) showMessage(string("Unable to start networking!")) subsys.StatusFatalError subsys.chirpSad waitcnt(clkfreq*10000 + cnt) reboot if NOT settings.getData(settings#NET_IPv4_ADDR,@stack,4) term.str(string("IPv4 ADDR: DHCP...")) repeat while NOT settings.getData(settings#NET_IPv4_ADDR,@stack,4) if ina[subsys#BTTNPin] reboot delay_ms(500) term.out($0A) term.out($00) term.str(string("IPv4 ADDR: ")) repeat i from 0 to 3 if i term.out(".") term.dec(byte[@stack][i]) term.out(13) if settings.getData(settings#NET_IPv4_DNS,@stack,4) term.str(string("DNS ADDR: ")) repeat i from 0 to 3 if i term.out(".") term.dec(byte[@stack][i]) term.out(13) subsys.StatusIdle subsys.chirpHappy if(!settings.findKey(settings#MISC_X10_HOUSE)) ' By default, use house 'H' settings.setByte(settings#MISC_X10_HOUSE,X10#HOUSE_H) X10.start(X10_ZC_PIN,X10_IN_PIN,X10_OUT_PIN) repeat i:=\httpServer subsys.click term.str(string("HTTP SERVER EXCEPTION ")) term.dec(i) term.out(13) socket.closeall PUB showMessage(str) term.str(string($1,$B,12,$C,$1)) term.str(str) term.str(string($C,$8)) PRI delay_ms(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932)) + cnt) VAR byte httpMethod[8] byte httpPath[128] byte httpQuery[128] byte httpHeader[32] byte buffer[128] byte buffer2[128] DAT HTTP_200 BYTE "HTTP/1.1 200 OK" CR_LF BYTE 13,10,0 HTTP_303 BYTE "HTTP/1.1 303 See Other",13,10,0 HTTP_404 BYTE "HTTP/1.1 404 Not Found",13,10,0 HTTP_411 BYTE "HTTP/1.1 411 Length Required",13,10,0 HTTP_501 BYTE "HTTP/1.1 501 Not Implemented",13,10,0 HTTP_401 BYTE "HTTP/1.1 401 Authorization Required",13,10,0 HTTP_CONTENT_TYPE_HTML BYTE "Content-Type: text/html; charset=utf-8",13,10,0 HTTP_CONNECTION_CLOSE BYTE "Connection: close",13,10,0 pri httpUnauthorized(authorized) socket.str(@HTTP_401) socket.str(@HTTP_CONNECTION_CLOSE) auth.generateChallenge(@buffer,127,authorized) socket.txMimeHeader(string("WWW-Authenticate"),@buffer) socket.str(@CR_LF) socket.str(@HTTP_401) PUB atoi(inptr):retVal | i,char retVal~ ' Skip leading whitespace repeat while BYTE[inptr] AND BYTE[inptr]==" " inptr++ repeat 10 case (char := BYTE[inptr++]) "0".."9": retVal:=retVal*10+char-"0" OTHER: quit pub httpServer | char, i, contentLength,authorized,queryPtr,house,unit,code repeat repeat while \socket.listen(80) == -1 if ina[subsys#BTTNPin] reboot delay_ms(100) socket.closeall next repeat while NOT socket.isConnected socket.waitConnectTimeout(100) if ina[subsys#BTTNPin] reboot ' If there isn't a password set, then we are by default "authorized" authorized:=NOT settings.findKey(settings#MISC_PASSWORD) http.parseRequest(socket.handle,@httpMethod,@httpPath) contentLength:=0 repeat while http.getNextHeader(socket.handle,@httpHeader,32,@buffer,256) if strcomp(@httpHeader,string("Content-Length")) contentLength:=numbers.fromStr(@buffer,numbers#DEC) elseif NOT authorized AND strcomp(@httpHeader,string("Authorization")) authorized:=auth.authenticateResponse(@buffer,@httpMethod,@httpPath) ' Authorization check ' You can comment this out if you want to ' be able to let unauthorized people see the ' front page. if authorized<>auth#STAT_AUTH httpUnauthorized(authorized) socket.close next queryPtr:=http.splitPathAndQuery(@httpPath) if strcomp(@httpMethod,string("GET")) hits++ if strcomp(@httpPath,string("/")) socket.str(@HTTP_200) socket.str(@HTTP_CONTENT_TYPE_HTML) socket.str(@HTTP_CONNECTION_CLOSE) socket.str(@CR_LF) indexPage elseif strcomp(@httpPath,string("/reboot")) if authorized<>auth#STAT_AUTH httpUnauthorized(authorized) socket.close next socket.str(@HTTP_200) socket.str(@HTTP_CONNECTION_CLOSE) socket.txmimeheader(string("Refresh"),string("8;url=/")) socket.str(@CR_LF) if strcomp(queryPtr,string("bootloader")) AND settings.findKey(settings#MISC_AUTOBOOT) socket.str(string("DISABLING AUTOBOOT",13,10)) settings.revert settings.removeKey(settings#MISC_AUTOBOOT) settings.commit socket.str(string("REBOOTING",13,10)) socket.close delay_ms(100) reboot elseif strcomp(@httpPath,string("/send")) socket.str(@HTTP_200) socket.str(@HTTP_CONNECTION_CLOSE) socket.txmimeheader(string("Refresh"),string("0;url=/")) socket.str(@CR_LF) house := settings.getByte(settings#MISC_X10_HOUSE) if http.getFieldFromQuery(queryPtr,string("house"),@buffer,10) house:=atoi(@buffer) if http.getFieldFromQuery(queryPtr,string("code"),@buffer,10) code:=atoi(@buffer) if http.getFieldFromQuery(queryPtr,string("unit"),@buffer,10) unit:=atoi(@buffer) X10.send_to_unit(house,unit,code) socket.str(string("sent house=")) socket.dec(house) socket.str(string(" unit=")) socket.dec(unit) socket.str(string(" code=")) socket.dec(code) else X10.send(house,code) socket.str(string("sent house=")) socket.dec(house) socket.str(string(" code=")) socket.dec(code) socket.str(@CR_LF) else socket.str(@HTTP_404) socket.str(@HTTP_CONNECTION_CLOSE) socket.str(@CR_LF) socket.str(@HTTP_404) else socket.str(@HTTP_501) socket.str(@HTTP_CONNECTION_CLOSE) socket.str(@CR_LF) socket.str(@HTTP_501) socket.close pri httpOutputLink(url,class,content) socket.str(string("")) socket.str(content) socket.str(string("")) pri httpOutputROMCode(p) repeat 8 socket.hex(byte[p++],2) pri indexPage | i, p, count1, count2 'term.str(string("Sending index page",13)) socket.str(string("X10 Bridge")) socket.str(string("")) socket.str(string("

")) socket.str(@productName) socket.str(string("

")) socket.str(string("

Default House Code:")) socket.dec(settings.getByte(settings#MISC_X10_HOUSE)) socket.str(string(" (Raw)

")) waitpeq(0,|Cycle Rate: ")) socket.str(fp.FloatToString(F.FDiv(F.FFloat(clkfreq),F.FFloat(count2-count1)))) socket.str(string("Hz

")) httpOutputLink(string("/send?code=3"),string("green button"),string("Lights On")) socket.str(string("
")) httpOutputLink(string("/send?unit=12&code=5"),string("green button"),string("Unit 1 On")) socket.str(string("
")) httpOutputLink(string("/send?unit=12&code=7"),string("red button"),string("Unit 1 Off")) socket.str(string("
")) httpOutputLink(string("/send?code=1"),string("red button"),string("All Off")) socket.str(string("
")) httpOutputLink(string("/reboot"),string("black button"),string("Reboot")) socket.str(string("
")) 'socket.str(string("

")) socket.str(string("

Other

")) httpOutputLink(@productURL,0,@productURL) socket.str(string("",13,10))