Project - Smart Socket

The Smart Socket is a Sonoff S20 (or successor) plug-in version of the wired Smart Switch - they both use the same script, the only difference is in the usage.
The Smart Socket uses the integral gpio0 push-button to differentiate between short or long button presses, whereas the Smart Switch is able to use gpio0 for a bi-state lever switch, which therefore makes On durations irrelevant (therefore no ability for short or long button-press timings).

The default script offers On/Off and Toggle, plus Delayed Cycle On/Off for a specified Duration, or Scheduled On/Off, or Thermostat control, or Sensor trigger.
The integrated hardware button provides a physical local Toggle, plus the device can be remotely controlled via web page or UDP.
The button surround allows an internal blue LED to show when the relay is On.
Similarly a green user LED shows any pending countdown timers.
A short button press toggles the relay (blue LED), whereas a long button press blinks out the IP address on the green LED.

This Smart Socket demo video will give an idea of what it can do and how to use it.

Refer to the Smart Switch project for a more detailed explanation of what this 'Smart' script does, and how it does it.
You may also want to refer to the TX RX Hack, which conveniently and safely offers 2 additional user gpio's for sensors etc,
It also offers a safe means of serial flashing with the device removed from the Mains, and without needing to dismantle it.


The Smart Socket can be used 'standalone' by itself, or can interact with other EasyNet devices if wished. 
So for example a Smart Switch might control one or more Smart Sockets (all using exactly the same script with identical network configuration).
EasyNet is entirely optional and transparent though, so simply ignore it if not needed.

An EasyNet device can be addressed by its Nodename, or by partial Groupname, or by its IP address, or "All". 
If no meaningfull nodename is assigned (because you forgot or couldn't be bothered at the time), the script will assign itself a unique nodename.
So consider a scenario where several EasyNet devices with the same default scripts all received arbitrary DHCP addresses from the router and you don't know anything about any of it !!
It is actually quite simple to find out what's what...
All EasyNet nodes will respond to the name "ALL", and all nodes will respond to "Reply", "Blink", and "BlinkIP" UDP instructions.
So sending "ALL REPLY" from the UDP console will cause all nodes to reply with their Nodename and IP address.
Once you know the nodenames you can send each in turn a "Blink" instruction to show their physical location by blinking their LED.

They already replied with their Nodename and IP address anyway, but sending "BlinkIP" to a Nodename would cause it to locally blink out its IP address on its LED.
The only reason for knowing device IP addresses is to connect to them from a browser for user management, because they are not needed for device interactions.
Note: Developed on 1.39 beta 1, and does not work on version 1.39 beta 2 because of a firmware bug, but should hopefully be fixed in the next release. 

Basic:
title$ = "EasyNet Smart Switch/Socket - by Electroguard - developed on Annex 1.39 beta 1"    
nodename$  = ""                          'Assign a unique node name of your choice (if you forget, it will be called "Node" + its node IP)
groupname$ = "Sonoff\Smart\Switch\Socket\Relay"    'concatenated group names are searched for a partial match
localIP$   = WORD$(IP$,1)
netIP$     = WORD$(localIP$,1,".") + "." + WORD$(localIP$,2,".") + "." + WORD$(localIP$,3,".") + "."
nodeIP$    = WORD$(localIP$,4,".")
udpport    = 5001                         'change to suit your own preference, but don't forget to do the same for all nodes
if nodename$ = "" then nodename$ = "Node" + nodeIP$
showsettings = 0
showtitle = 1
showID = 0                                  '=1 to show local identity info         
showbuttons = 1                          '=1 to show onscreen system buttons
showclock = 0                              '=1 to show clock and schedule options
showscheduled = 0                      '=1 to show scheduled alarm On and Off times
showcycled = 0                            '=1 to show cycleOn and cycleOff options
showswmodes = 0                       '=1 to show harware button and switch mode checkboxes
showtools = 0                               '=1 to show onscreen system buttons
showtherm = 0                              '=1 to show temperature display
showstat = 0                                 '=1 to enable thermostat options
'filename$ = word$(BAS.FILENAME$,1,".") + ".ini"      'Un-comment this line to save settings to 'this scriptname'.ini
filename$ = "/program/smartswitch.ini"                         'Uncomment this line to save to a specfied file
fontpath$ = "/font/"                        'path to optional font file
fontfile$ = "dig7monoitalic.ttf"        'filename.ext of optional font file
instructionslist$ = "Reply Report BlinkIP Blink Save Load "      'List of Subdir branches available as remote triggers
instructionslist$ = instructionslist$ + "Relay1On Relay1Off Toggle1 Cycle1On Cycle1Off"     'List of Subdir branches available as remote triggers
setpoint = 22                                  'thermostat setpoint
statlist$ = "fan,heater"           
statmode$ = "fan"                          'fan switches relay on if temp above setpoint, heater switches relay on if temp below setpoint.
dallaspin = 1                                  'Dallas 1-wire temperature sensor pin
temp$ = str$(val(tempr$(dallaspin,1)),"%2.1f")
newtemp$ = ""
enablestat = 0                                '=1 to enable thermostat switching
enablescheduledOn  = 0                '=1 to enable scheduled On time
enablescheduledOff = 0                 '=1 to enable scheduled Off time
ontime$ = "8:01"
offtime$ = "8:02"
unitslist$ = "secs, mins, hours, days"
secs = 1: mins = secs * 60: hours = mins * 60: days = hours * 24
enabledelayon = 0                          '=1 enable cycleOn delay
ondelay = 7                                     'cycleOn delay
ondelunits$ = "days"
ondelaycountdown = -1
enabletimedon =                          '=1 enable cycleOn duration
onduration = 1                                'cycleOn duration
ondurunits$ = "mins"
ondurationcountdown = -1
enabledelayoff = 1                           '=1 enable cycleOff delay
offdelay = 10                                    'cycleOff duration
offdelunits$ = "secs"
offdelaycountdown = -1
enabletimedoff = 1                           '=1 enable cycleOff duration
offduration = 30                               'cycleOff duration
offdurationcountdown = -1
offdurunits$ = "secs"
instruction$ = ""                               'variable to hold incoming instruction
RXmsg$ = ""                                    'variable to hold incoming message
data$ = ""                                         'variable to hold any incoming data which follows the instruction
retryq$ = ""                                       'variable to hold all unexpired messages still waiting to be acknowledged
qdelimiter$ = "|"                                'separates messages in the retryq
time2live = 30                                   'sent-message unacknowledged lifetime in seconds
led1pin = 13: led1off = 1: pin.mode led1pin, output: pin(led1pin) = led1off
relay1pin = 12: relay1noff = 0: pin.mode relay1pin, output: pin(relay1pin) = relay1noff         'using active high qpio12 for relay1
switchpin = 3: switchoff = 1:  pin.mode switchpin, input, pullup                                              'switch normally high active low
switchmode = 1                                '1=bi-state lever switch, 0=momentary press to toggle button
interrupt switchpin, switched
buttonpin = 0: pin.mode buttonpin, input, pullup                                                                     'using active low gpio0 button
buttonmode = 0                                '1=bi-state lever switch, 0=momentary press to toggle button
interrupt buttonpin, pressed
start=0: stop=0                                 'used by button-pressed subroutine to differentiate between short and long presses
debounce = 100
longpress=3000                               'longpress set high at 3 secs to minimise accidental triggering of blinkIP
ledstat$ = "green"
blinks = 10                                        'blink default number of blinks, can be over-ridden by sending "nodename blink number_of_blinks"
'gosub load                                       'ini file mechanism is not fully implemented
gosub paint
onhtmlchange changed
onhtmlreload paint
timer0 1000, ticker                         
timer1 1000, Retry                            'periodic timer to keep resending unACKed msgs until they expire                      
udp.begin(udpport)
onudp udpRX
wlog "Started: " + time$ + " on " + date$
wait

paint:
cls
autorefresh 1000
a$ = a$ + |<br><div id='message' data-var='clicked' onclickx='cmdButton(this)' style='display: table; margin-right:auto;margin-left:auto;text-align:center;'>|
if showtitle = 1 then a$ = a$ + title$ + "<br><br>"
if showID = 1 then
 a$ = a$ + |<table align='center'><tr><td>|
 a$ = a$ + |Node name:</td><td>| + textbox$(nodename$,"tbname") + |</td></tr><tr><td>|
 a$ = a$ + cssid$("tbname", "color:Darkcyan;font-size:1.2em;width:150px;")
 a$ = a$ + |local IP:</td><td>| + localIP$ + |</td></tr><tr><td>|
 a$ = a$ + |UDP port:</td><td>| + textbox$(udpport,"tb40") + |</td></tr></td></tr></table><br><br>|
endif
if showbuttons = 1 then
 a$ = a$ + button$("Instant On",relay1on) + string$(9,"&nbsp;") + button$("Toggle", toggle1, "butled") + string$(9,"&nbsp;") + button$("Instant Off",relay1off) + |<br><br>|
 a$ = a$ + cssid$("butled", "height:3em; font-size:1.5em; border-radius:.4em; padding:.5em; color:white; background:" + ledstat$ + ";")
endif
if showclock = 1 then
 a$ = a$ + |<div  style='display: table; margin-right:auto;margin-left:auto;text-align:center;borderx:1px solid gray;'>|
 a$ = a$ + |<style> @font-face { font-family: myfont; src: url('| + fontpath$ + fontfile$ + |');} </style><br>|
 a$ = a$ + |<div id='clock' style='font-family:myfont;background:lightcyan;color:dimgray;font-size:2.9em;border:1px solid gray;text-align:center;|
 a$ = a$ + |display: table; margin-right:auto;margin-left:auto;padding-left:.4em;padding-right:.4em;'>| + time$ + |</div><br>|
endif
if showscheduled = 1 then
 a$ = a$ + |<div  style='display: table; margin-right:auto;margin-left:auto;text-align:center;borderx:1px solid gray;'>|
 a$ = a$ + checkbox$(enablescheduledon) + " " + |On Time:| + textbox$(ontime$,"tb40") + string$(9,"&nbsp;")
 a$ = a$ + checkbox$(enablescheduledoff) + " " + |Off Time:</td><td>| + textbox$(offtime$,"tb40") + |<br>|
 a$ = a$ + |</div>|
endif
html a$
pause 200
a$ = ""
if (showtherm = 1) or (showstat = 1) then
 a$ = a$ + |<div  style='display: table; margin-right:auto;margin-left:auto;text-align:center;borderx:1px solid gray;'>|
 a$ = a$ + |<br><div id='temp' style='font-family: myfont;background:LightGoldenRodYellow ;color:dimgray;font-size:1.8em;text-align:center;border:1px solid gray;|
 a$ = a$ + |display: table; margin-right:auto;margin-left:auto;border-radius:3em;padding-left:.4em;padding-right:.3em;'>| + temp$ + "&#730;" + |</div>|
 a$ = a$ + "<br>"
 if showstat = 1 then
  a$ = a$ + button$(" < ", statdown) + " " + textbox$(setpoint,"tb30") + " " + button$(" > ", statup) + "<br>"
  a$ = a$ + checkbox$(enablestat) + listbox$(statmode$,statlist$,"tb80") + "<br><br>"
 endif
 a$ = a$ + |</div>|
endif
if showcycled = 1 then
 a$ = a$ + |<div  style='display: table; margin-right:auto;margin-left:auto;text-align:center;borderx:1px solid gray;'>|
 a$ = a$ + |<table align='center'><tr><td>|
 a$ = a$ + button$("Controlled On ", cycle1on) + string$(9,"&nbsp;") + |</td><td>| + "On delay " + |</td><td>|
 a$ = a$ + " " + checkbox$(enabledelayon) + |</td><td>| + " " + textbox$(ondelay,"tb40") + |</td><td>| + listbox$(ondelunits$,unitslist$,"tb60") + |</td><td>|
 a$ = a$ + string$(9,"&nbsp;") + "  On  duration " + |</td><td>| + checkbox$(enabletimedon) + |</td><td>| + " " + textbox$(onduration,"tb40") + |</td><td>| + listbox$(ondurunits$,unitslist$,"tb60") + |</td></tr><br><tr><td>|
 a$ = a$ + button$("Controlled Off ", cycle1off) + string$(9,"&nbsp;") + |</td><td>| + "Off delay " + |</td><td>|
 a$ = a$ + " " + checkbox$(enabledelayoff) + |</td><td>| + " " + textbox$(offdelay,"tb40") + |</td><td>| + listbox$(offdelunits$,unitslist$,"tb60") + |</td><td>|
 a$ = a$ + string$(9,"&nbsp;") + "  Off duration " + |</td><td>| + checkbox$(enabletimedoff) + |</td><td>| + " " + textbox$(offduration,"tb40") + |</td><td>| + listbox$(offdurunits$,unitslist$,"tb60")  + |</td></tr></table><br>|
 a$ = a$ + |</div>|
endif
if showtools = 1 then
 a$ = a$ + |<div  style='display: table; margin-right:auto;margin-left:auto;text-align:center;borderx:1px solid gray;'>|
 a$ = a$ + "<br>" + button$("Blink", blink) + "  " + textbox$(blinks,"tb40") + string$(9,"&nbsp;")
 a$ = a$ + button$("Blink IP", blinkip) + string$(9,"&nbsp;")
 a$ = a$ + button$("Send", sendudp) + string$(9,"&nbsp;")
 a$ = a$ + button$("Save settings", save) + "<br>"
 a$ = a$ + |</div>|
endif
if showswmodes = 1 then
 a$ = a$ + "<br> gpio0  On/Off flip switch " + checkbox$(buttonmode) + " or momentary toggle button<br>"
 a$ = a$ + " gpio" + str$(switchpin) + " On/Off flip switch " + checkbox$(switchmode) + " or momentary toggle button<br>"
endif
a$ = a$ + "<br>ShowSettings:" + checkbox$(showsettings)
if showsettings = 1 then
 a$=a$+", ShowTitle:"+checkbox$(showtitle)+", ShowID:"+checkbox$(showid)+", Buttons:"+checkbox$(showbuttons)
 a$=a$+", ShowClock:"+checkbox$(showclock)+", ShowScheduled:"+checkbox$(showscheduled)
 a$=a$+", ShowTherm:"+checkbox$(showtherm)+", ShowStat:"+checkbox$(showstat)
 a$=a$+", ShowCycled:"+checkbox$(showcycled)+", ShowTools:"+checkbox$(showtools)+", ShowSWmodes:"+checkbox$(showswmodes)+"<br>"+"<br>"
endif
a$ = a$ + cssid$("tb30", "width:30; text-align:center; color:teal; background:GhostWhite;")
a$ = a$ + cssid$("tb40", "width:40; text-align:center; color:teal; background:GhostWhite;")
a$ = a$ + cssid$("tb60", "width:60; text-align:center; color:teal; background:GhostWhite;")
a$ = a$ + cssid$("tb80", "width:80; text-align:center; color:teal; background:GhostWhite;")
a$ = a$ + |</div>|
html a$
a$ = ""
return

settings:
if visibility$ = "Hide" then visibility$ = "Show" else visibility$ = "Hide"
refresh
return

changed:
ch$ = HtmlEventVar$
if instr(ch$,"show") = 1 then gosub paint
if len(word$(ontime$,2,":")) = 1 then ontime$ = replace$(ontime$,":",":0")
if len(word$(offtime$,2,":")) = 1 then offtime$ = replace$(offtime$,":",":0")
return

ticker:
if showclock = 1 then jscall |_$('clock').innerHTML = "| + time$ + |"|        ' updates the digital clock display
if (showtherm = 1) or (enablestat = 1) then
 newtemp$ = str$(val(tempr$(dallaspin,1)),"%2.1f")
 if (newtemp$ <> temp$) then
  temp$ = newtemp$
  jscall |_$('temp').innerHTML = "| + temp$ + "&#730;" + |"|        ' updates the temp display
 endif
endif
if enablestat = 1 then
 if val(temp$) < setpoint then
  'turn heater on or fan off
  if (statmode$ = "heater") and (pin(relay1pin) = relay1noff) then
   if relay1noff = 0 then pin(relay1pin) = 1 else pin(relay1pin) = 0
   html cssid$("butled", "background:red;")
  endif
  if (statmode$ = "fan") and (pin(relay1pin) <> relay1noff) then
   pin(relay1pin) = relay1noff
   html cssid$("butled", "background:green;")
  endif
 endif
 if (val(temp$) > setpoint) then
  if (statmode$ = "heater") and (pin(relay1pin) <> relay1noff) then
   pin(relay1pin) = relay1noff
   html cssid$("butled", "background:green;")
  endif
  if (statmode$ = "fan") and (pin(relay1pin) = relay1noff) then
   pin(relay1pin) = 1 - relay1noff
   html cssid$("butled", "background:red;")
  endif
 endif
endif
if (enablescheduledon = 1) and (pin(relay1pin) = relay1noff) then
 if (val(word$(ontime$,1,":")) = val(word$(time$,1,":"))) and (val(word$(ontime$,2,":")) = val(word$(time$,2,":"))) then gosub relay1on
endif
if (enablescheduledoff = 1) and (pin(relay1pin) <> relay1noff) then
 if (val(word$(offtime$,1,":")) = val(word$(time$,1,":"))) and (val(word$(offtime$,2,":")) = val(word$(time$,2,":"))) then gosub relay1off
endif
if ondelaycountdown >= 0 then
 if pin(led1pin) = 0 then pin(led1pin) = 1 else pin(led1pin) = 0
 if ondelaycountdown > 0 then
  ondelaycountdown = ondelaycountdown -1
 else
  ondelaycountdown = -1
  gosub relay1on
  COMMAND "m=" + ondurunits$
  if enabletimedon = 1 then ondurationcountdown = onduration * m
 endif
endif
if ondurationcountdown >= 0 then
 if pin(led1pin) = 0 then pin(led1pin) = 1 else pin(led1pin) = 0
 if ondurationcountdown > 0 then
  ondurationcountdown = ondurationcountdown -1
 else
  ondurationcountdown = -1
  gosub relay1off
 endif
endif
if offdelaycountdown >= 0 then
 if pin(led1pin) = 0 then pin(led1pin) = 1 else pin(led1pin) = 0
 if offdelaycountdown > 0 then
  offdelaycountdown = offdelaycountdown -1
 else
  offdelaycountdown = -1
  gosub relay1off
  COMMAND "m=" + offdurunits$
  if enabletimedoff = 1 then offdurationcountdown = offduration * m
 endif
endif
if offdurationcountdown >= 0 then
 if pin(led1pin) = 0 then pin(led1pin) = 1 else pin(led1pin) = 0
 if offdurationcountdown > 0 then
  offdurationcountdown = offdurationcountdown -1
 else
  offdurationcountdown = -1
  gosub relay1on
 endif
endif
return

statup:
setpoint = setpoint + 1
newtemp$ = ""
refresh
return

statdown:
setpoint = setpoint - 1
if setpoint < 0 then setpoint = 0
newtemp$ = ""
refresh
return

udpRX:
RXmsg$ = udp.read$
if ucase$(word$(RXmsg$,1)) = "ACK" then
 gosub ACK   'echoed reply from successfully received message, original msg can be removed from queue
else
 target$ = ucase$(word$(RXmsg$,1))                  'Target may be NodeName or GroupName or "ALL" or localIP address
 if (target$=localIP$) OR (target$=ucase$(nodename$)) OR (instr(ucase$(groupname$),target$)>0) OR (target$="ALL") then
  instruction$ = trim$(ucase$(word$(RXmsg$,2)))     'Instruction is second word of message
  data$ = "": getdata data$,RXmsg$," ",2            'extract any data that follows the instruction
  if word.find(ucase$(instructionslist$),instruction$) > 0 then
   if (ucase$(instruction$) <> "ACK") and (instr(ucase$(data$),"ID=") > 0) then
    udp.reply "ACK " + RXmsg$                        'ACKnowledge the incoming msg
   endif
   gosub instruction$                                         'branch to action the corresponding instruction subroutine
  else
   udp.reply RXmsg$ + " INSTRUCTION NOT RECOGNISED"
  endif  'word.find
 endif   '(target$=localIP$)
endif     'ACK
return

ACK:
msg$ = "": getdata msg$, RXmsg$, " ", 1
pos = word.find(retryq$,msg$,qdelimiter$)
if pos > 0 then retryq$ = word.delete$(retryq$,pos,qdelimiter$)
return

RETRY:
if word.count(retryq$, qdelimiter$) > 0 then
if retryq$ <> "" then wlog "queue=" + retryq$
 msg$ = word$(retryq$,1,qdelimiter$)                  'grab first unACKed  msg in the queue
 retryq$ = word.delete$(retryq$,1,qdelimiter$)     'chop msg off front of queue
 expire$ = ""
 WordParse expire$, msg$, "ID=", " "                   'parse out ID= expire time
 if msg$ <> "" then                                                'compare expire time to current unix time
  if dateunix(date$) + timeunix(time$) > val(expire$) then
   Send "LOG ERROR: Node " + Nodename$ + " FAILED SEND - " + msg$ + " not ACKnowledged"
  else
   retryq$ = retryq$ + msg$ + qdelimiter$
   udp.write netip$ + "255", udpport, msg$
   wlog "retry " + msg$
  endif
 endif
endif
return

sendudp:
sendmsg$ = sendmsg$ + " ID=" + str$(dateunix(date$) + timeunix(time$) + time2live, "%10d", 1)
retryq$ = retryq$ + sendmsg$ + qdelimiter$
udp.write netip$ + "255", udpport, sendmsg$
return

sub SendQ(sendmsg$)    
sendmsg$ = sendmsg$ + " ID=" + str$(dateunix(date$) + timeunix(time$) + time2live, "%10d", 1)
retryq$ = retryq$ + sendmsg$ + qdelimiter$
udp.write netip$ + "255", udpport, sendmsg$
end sub

sub Send(sendmsg$)
udp.write netip$ + "255", udpport, sendmsg$
end sub

sub GetData(ret$, v$, sep$, pos)  'extracts everything from the msg after the Instruction and puts into data$ (thanks cicciocb)
local i, p, q
p = 1
for i = 1  to pos
 p = instr(p + 1, v$, sep$)
 if p > 0 then p = p + len(sep$)
next i
if p = 0 then
 ret$ = ""
else
 q = instr(p+1, v$, sep$)
 if q = 0 then q = 999
 ret$ = mid$(v$, p)
end if
end sub

sub WordParse(ret$, full$, search$, sep$)  'extracts value from option=value (thanks cicciocb)
local p, b$
p = instr(full$, search$)
if p <> 0 then
 b$ = mid$(full$, p + len(search$))
 ret$ = word$(b$, 1, sep$)
else
 ret$ = ""
end if
end sub

load:
' Loads settings from file ... not yet fully implemented
a$ = ""
if FILE.EXISTS(filename$) > 0 then
 a$ = FILE.READ$(filename$)
 if WORD.GETPARAM$(a$,"nodename") <> ""  then nodename$ = WORD.GETPARAM$(a$,"nodename")
 if WORD.GETPARAM$(a$,"ondelay") <> ""  then ondelay = val(WORD.GETPARAM$(a$,"ondelay"))
 if WORD.GETPARAM$(a$,"onduration") <> ""  then onduration = val(WORD.GETPARAM$(a$,"onduration"))
 if WORD.GETPARAM$(a$,"offdelay") <> ""  then offdelay = val(WORD.GETPARAM$(a$,"offdelay"))
 if WORD.GETPARAM$(a$,"offduration") <> ""  then offduration = val(WORD.GETPARAM$(a$,"offduration"))
 if WORD.GETPARAM$(a$,"udpport") <> ""  then udpport = val(WORD.GETPARAM$(a$,"udpport"))
endif
return

save:
' Saves settings to file ... not yet fully implemented
a$ = ""
if FILE.EXISTS(filename$) > 0 then a$ = FILE.READ$(filename$)
WORD.SETPARAM a$, "nodename", nodename$
WORD.SETPARAM a$, "ondelay", str$(ondelay)
WORD.SETPARAM a$, "onduration", str$(onduration)
WORD.SETPARAM a$, "offdelay", str$(offdelay)
WORD.SETPARAM a$, "offduration", str$(offduration)
WORD.SETPARAM a$, "udpport", str$(udpport)
FILE.SAVE filename$, a$
return

blink:
if data$ <> "" then blinks = val(data$)
ledstate = pin(led1pin)
pin(led1pin) = led1off
pause 200
for count = 1 to blinks
if led1off = 1 then pin(led1pin) = 0 else pin(led1pin) = 1
pause 800
pin(led1pin) = led1off
pause 200
next count
pause 2000
pin(led1pin) = ledstate  'Restore LED state to its previous state
return

blinkip:
ledstate = pin(led1pin)
blinkon = 150
blinkoff = 300
blinkpause = 1000
blinkgap = 1400
pin(led1pin) = led1off
pause blinkpause
for pos = 1 to len(localIP$)
 digitchr$ = mid$(localIP$,pos,1)
 if digitchr$ = "." then
  pause blinkgap
 else
  if digitchr$ = "0" then digit = 10 else digit = val(digitchr$)
  for count = 1 to digit
   if led1off = 0 then pin(led1pin) = 1 else pin(led1pin) = 0
   pause blinkon
   if led1off = 0 then pin(led1pin) = 0 else pin(led1pin) = 1
   pause blinkoff
  next count
  pause blinkpause
 end if
next pos
pause blinkgap
pin(led1pin) = ledstate
return

REPLY:
udp.reply "Reply from " + Nodename$
return

REPORT:
udp.reply "Report from " + Nodename$ + " " + instructionslist$
return

pressed:
interrupt buttonpin, off
pause debounce
if pin(buttonpin) = 0 then start = millis else stop = millis
if buttonmode = 1 then
 if pin(buttonpin) = 0 and (pin(relay1pin) = relay1noff) then gosub relay1on
 if pin(buttonpin) = 1 and (pin(relay1pin) <> relay1noff) then gosub relay1off
else
 if stop > start then
  if stop - start < longpress then gosub toggle1 else gosub blinkip
 endif
endif
interrupt buttonpin, pressed
return

switched:
interrupt switchpin, off
pause debounce
if pin(switchpin) <> switchoff then
 if switchmode = 1 then gosub relay1on else gosub toggle1
else
 if switchmode = 1 then gosub relay1off
endif
interrupt switchpin, switched
return

relay1on:
if pin(relay1pin) = relay1noff then
 if relay1noff = 0 then pin(relay1pin) = 1 else pin(relay1pin) = 0
endif
indicator = 0
pin(led1pin) = 1-led1off
html cssid$("butled", "background:red;")
ondelaycountdown = -1
offdelaycountdown = -1
ondurationcountdown = -1
offdurationcountdown = -1
return

relay1off:
if pin(relay1pin) <> relay1noff then pin(relay1pin) = relay1noff
indicator = 1
pin(led1pin) = led1off
html cssid$("butled", "background:green;")
ondelaycountdown = -1
offdelaycountdown = -1
ondurationcountdown = -1
offdurationcountdown = -1
return

cycle1on:
COMMAND "m=" + ondelunits$
if enabledelayon > 0 then ondelaycountdown = ondelay * m else ondelaycountdown = 0
return

cycle1off:
COMMAND "m=" + offdelunits$
if enabledelayoff > 0 then offdelaycountdown = offdelay * m else offdelaycountdown = 0
return

toggle1:
if pin(relay1pin) = relay1noff gosub relay1on else gosub relay1off
enablescheduledon = 0
enablescheduledoff = 0
enablestat = 0
enabledelayon = 0
enabledelayoff = 0
enabletimedon = 0
enabletimedoff = 0
ondelaycountdown = -1
offdelaycountdown = -1
ondurationcountdown = -1
offdurationcountdown = -1
return

END   '-------------------- End ---------------------







All of the functionality shown below is available... most is disabled by default but can be enabled individually 'as needed'.
 

See the Smart Switch project for details of how to download and install the 7-segment digital web fonts.