Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jshatch
Forked from ccutrer/_api.txt
Created October 26, 2019 02:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jshatch/34b3d1929b642ed53b4cc30013529778 to your computer and use it in GitHub Desktop.
Save jshatch/34b3d1929b642ed53b4cc30013529778 to your computer and use it in GitHub Desktop.
EverLights API
The app always sends a nocache param, which appears to be time since the app launched. It's ignored by the control box.
Color is just RGB. 255,255,255 = 0xffffff. 0,255,0 = 0x00ff00
Modes:
all modes can take a value between 0-255. only one of tw, cf, cr, wi, ra can be set at once
tw: twinkle
fa: fade
bl: blink
st: strobe
cf: chase forward
cr: chase reverse
wi: wipe
ra: random
rb: ???
For pattern names, spaces are saved as underscores (you can't save underscores)
User-Agent: everlights2/7 CFNetwork/978.0.7 Darwin/18.6.0
GET /status/get?nocache=534.999015
{"stsCnt":20,"ch1Cnt":402,"ch2Cnt":201,"version":"2.3.0","mac":"2C:3A:E8:FF:FF:FF","autopilot":0,"epoch":1560017691,"tzOffset":-7,"stsActive":1,"ch1Active":0,"ch2Active":0,"stsInd":1,"lightingStatus":0,"ssid":"Cutrer","rssi":-58,"ip":"192.168.10.10","mode":1,"status":"connected","rebootCnt":21,"loopsPerSecond":13422,"writeDuration":19327}
GET /epoch/set?epoch=1560017696&nocache=567.472399
{"success":true}
GET /ptrn/all
{"patterns":["NEW_YEAR'S","VALENTINE'S_DAY","ST_PATRICK'S_DAY","EASTER","MOTHER'S_DAY","FATHER'S_DAY","HALLOWEEN","VETERANS_DAY","THANKSGIVING","CHRISTMAS","Color_splat","My_Christmas","INDEPENDENCE_DAY"],"cnt":13}
GET /ptrn/get?id=NEW_YEAR'S
{"len":3,"ptrn":[16733952,16777215,16733952],"modes":{"fa":0,"bl":0,"st":0,"cf":0,"cr":0,"wi":0,"ra":0,"tw":0,"rb":0},"name":"NEW_YEAR'S"}
GET /sch/getchannel?channel=1
{"schedules":[]}
GET /ptrn/set?channel=3&data=%7B%22len%22:4,%22ptrn%22:[16777215,16711680,16777215,45056],%22modes%22:%7B%22fa%22:0,%22bl%22:0,%22st%22:0,%22cf%22:0,%22cr%22:0,%22wi%22:0,%22ra%22:0,%22tw%22:0,%22rb%22:0%7D,%22name%22:%22%22%7D
{"success":true}
GET /status/get
{"stsCnt":20,"ch1Cnt":402,"ch2Cnt":201,"version":"2.3.0","mac":"2C:3A:E8:FF:FF:FF","autopilot":0,"epoch":1560018587,"tzOffset":-7,"stsActive":1,"ch1Active":1,"ch2Active":1,"stsInd":1,"lightingStatus":0,"ssid":"Cutrer","rssi":-58,"ip":"192.168.10.10","mode":1,"status":"connected","rebootCnt":21,"loopsPerSecond":13099,"writeDuration":19660}
turn lights off
GET /ptrn/clear?channel=0
GET /ptrn/clear?channel=1
GET /ptrn/clear?channel=2
GET /ptrn/clear?channel=3
save pattern
GET /ptrn/sv?id=My_Christmas2&data=%7B%22len%22:4,%22ptrn%22:[16777215,16711680,16777215,45056],%22modes%22:%7B%22fa%22:0,%22bl%22:0,%22st%22:0,%22cf%22:0,%22cr%22:0,%22wi%22:0,%22ra%22:154,%22tw%22:0,%22rb%22:0%7D,%22name%22:%22My_Christmas2%22%7D
delete a pattern
GET /ptrn/del?id=MY_CHRUSTMAS
rule "Any rule that runs daily at sunset or whenever you want the lights to come on"
when
...
then
// Calculate if Everlights should be on
var holiday = executeCommandLine("/var/lib/openhab2/holidays.rb", 500)
if (holiday != "") {
Everlights_Preset.sendCommand(holiday)
Everlights_Switch.sendCommand(ON)
}
end
// don't forget a rule to turn them off
Switch Everlights_Switch "Everlights" <light> (gAllOff)
String EverlightsZone1_Data
String EverlightsZone2_Data
Group gEverlights
Group gEverlightsExclusive
String Everlights_Preset "Pattern"
Switch Everlights_PatternChanging
Number Everlights_Count "Pattern Length [%s]" (gEverlights)
Color Everlights1_RGB "Color 1" (gEverlights)
Color Everlights2_RGB "Color 2" (gEverlights)
Color Everlights3_RGB "Color 3" (gEverlights)
Color Everlights4_RGB "Color 4" (gEverlights)
Color Everlights5_RGB "Color 5" (gEverlights)
Color Everlights6_RGB "Color 6" (gEverlights)
Color Everlights7_RGB "Color 7" (gEverlights)
Color Everlights8_RGB "Color 8" (gEverlights)
Color Everlights9_RGB "Color 9" (gEverlights)
Color Everlights10_RGB "Color 10" (gEverlights)
Number Everlights_Twinkle "Twinkle" (gEverlights, gEverlightsExclusive)
Number Everlights_Fade "Fade" (gEverlights)
Number Everlights_Blink "Blink" (gEverlights)
Number Everlights_Strobe "Strobe" (gEverlights)
Number Everlights_Chase "Chase" (gEverlights, gEverlightsExclusive)
Number Everlights_ChaseReverse "Reverse Chase" (gEverlights, gEverlightsExclusive)
Number Everlights_Wipe "Wipe" (gEverlights, gEverlightsExclusive)
Number Everlights_Random "Random" (gEverlights, gEverlightsExclusive)
#!/usr/bin/env ruby
require 'cgi'
require 'json'
require 'net/http'
def hsv_to_rgb(h, s, v)
h, s, v = h.to_f/360, s.to_f/100, v.to_f/100
h_i = (h*6).to_i
f = h*6 - h_i
p = v * (1 - s)
q = v * (1 - f*s)
t = v * (1 - (1 - f) * s)
r, g, b = v, t, p if h_i==0
r, g, b = q, v, p if h_i==1
r, g, b = p, v, t if h_i==2
r, g, b = p, q, v if h_i==3
r, g, b = t, p, v if h_i==4
r, g, b = v, p, q if h_i==5
[(r*255).to_i, (g*255).to_i, (b*255).to_i]
end
def convert_rgb(string)
string = "0,0,0" if string == "NULL"
hsv_to_rgb(*string.split(',')).inject(0) do |result, value|
result *= 256
result + value.to_i
end
end
def normalize_pattern(pattern, reverse: false)
modes = { fa: 0, bl: 0, st: 0, cf: 0, cr: 0, wi: 0, ra: 0, tw: 0, rb: 0 }.merge(pattern[:modes] || {})
pattern = pattern[:ptrn]
if reverse
temp = modes[:cf]
modes[:cf] = modes[:cr]
modes[:cr] = temp
pattern = pattern.reverse
end
CGI.escape({ len: pattern.length, ptrn: pattern, modes: modes }.to_json)
end
reverse = !!ARGV.delete('--reverse')
pattern = {}
Net::HTTP.start('localhost', 8080) do |http|
pattern[:len] = http.get("/rest/items/Everlights_Count/state").body.to_i
pattern[:ptrn] = (1..pattern[:len]).map do |i|
convert_rgb(http.get("/rest/items/Everlights#{i}_RGB/state").body)
end
modes = pattern[:modes] = {}
modes[:tw] = http.get("/rest/items/Everlights_Twinkle/state").body.to_i
modes[:fa] = http.get("/rest/items/Everlights_Fade/state").body.to_i
modes[:bl] = http.get("/rest/items/Everlights_Blink/state").body.to_i
modes[:st] = http.get("/rest/items/Everlights_Strobe/state").body.to_i
modes[:cf] = http.get("/rest/items/Everlights_Chase/state").body.to_i
modes[:cr] = http.get("/rest/items/Everlights_ChaseReverse/state").body.to_i
modes[:wi] = http.get("/rest/items/Everlights_Wipe/state").body.to_i
modes[:ra] = http.get("/rest/items/Everlights_Random/state").body.to_i
end
puts normalize_pattern(pattern, reverse: reverse)
rule "Everlights On"
when
Item Everlights_Switch changed to ON
then
if (EverlightsZone1_Data.state != "") {
sendHttpGetRequest("http://192.168.85.223/ptrn/set?channel=1&data=" + EverlightsZone1_Data.state)
}
if (EverlightsZone2_Data.state != "") {
sendHttpGetRequest("http://192.168.85.223/ptrn/set?channel=2&data=" + EverlightsZone2_Data.state)
}
end
rule "Everlights Off"
when
Item Everlights_Switch changed to OFF
then
sendHttpGetRequest("http://192.168.85.223/ptrn/clear?channel=3")
end
rule "Everlights zone1 change"
when
Item EverlightsZone1_Data changed
then
if (Everlights_Switch.state == ON && EverlightsZone1_Data.state != "") {
sendHttpGetRequest("http://192.168.85.223/ptrn/set?channel=1&data=" + EverlightsZone1_Data.state)
}
end
rule "Everlights zone2 change"
when
Item EverlightsZone2_Data changed
then
if (Everlights_Switch.state == ON && EverlightsZone2_Data.state != "") {
sendHttpGetRequest("http://192.168.85.223/ptrn/set?channel=2&data=" + EverlightsZone2_Data.state)
}
end
rule "Everlights Pattern Changed"
when
Member of gEverlights changed
then
// Delay updating this until everything is changed from a preset
if (Everlights_PatternChanging.state != ON) {
EverlightsZone1_Data.postUpdate(executeCommandLine("/var/lib/openhab2/everlights.rb", 500))
EverlightsZone2_Data.postUpdate(executeCommandLine("/var/lib/openhab2/everlights.rb --reverse", 500))
Everlights_Pattern.postUpdate("")
}
end
rule "Everlights exclusive modes"
when
Member of gEverlightsExclusive changed
then
gEverlightsExclusive.members.forEach[item|
if (item != triggeringItem) {
item.postUpdate(0)
}
]
end
rule "Everlights preset pattern"
when
Item Everlights_Preset changed
then
Everlights_PatternChanging.postUpdate(ON)
if (Everlights_Preset.state == "Valentine's Day") {
Everlights_Count.postUpdate(6)
Everlights1_RGB.postUpdate(HSBType.fromRGB(255, 0, 0))
Everlights2_RGB.postUpdate(HSBType.fromRGB(255, 255, 255))
Everlights3_RGB.postUpdate(HSBType.fromRGB(255, 0, 0))
Everlights4_RGB.postUpdate(HSBType.fromRGB(252, 0, 255))
Everlights5_RGB.postUpdate(HSBType.fromRGB(252, 0, 255))
Everlights6_RGB.postUpdate(HSBType.fromRGB(252, 0, 255))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
Everlights_Chase.postUpdate(100)
} else if (Everlights_Preset.state == "St. Patrick's Day") {
Everlights_Count.postUpdate(3)
Everlights1_RGB.postUpdate(HSBType.fromRGB(0, 255, 65))
Everlights2_RGB.postUpdate(HSBType.fromRGB(0, 255, 65))
Everlights3_RGB.postUpdate(HSBType.fromRGB(0, 255, 0))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
Everlights_Chase.postUpdate(100)
} else if (Everlights_Preset.state == "Easter") {
Everlights_Count.postUpdate(5)
Everlights1_RGB.postUpdate(HSBType.fromRGB(253, 19, 255))
Everlights2_RGB.postUpdate(HSBType.fromRGB(249, 255, 32))
Everlights3_RGB.postUpdate(HSBType.fromRGB(255, 1, 65))
Everlights4_RGB.postUpdate(HSBType.fromRGB(90, 255, 18))
Everlights5_RGB.postUpdate(HSBType.fromRGB(41, 73, 255))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
gEverlightsExclusive.sendCommand(0)
} else if (Everlights_Preset.state == "Patriotic") {
Everlights_Count.postUpdate(6)
Everlights1_RGB.postUpdate(HSBType.fromRGB(0, 0, 255))
Everlights2_RGB.postUpdate(HSBType.fromRGB(255, 0, 0))
Everlights3_RGB.postUpdate(HSBType.fromRGB(255, 255, 255))
Everlights4_RGB.postUpdate(HSBType.fromRGB(252, 0, 0))
Everlights5_RGB.postUpdate(HSBType.fromRGB(0, 0, 255))
Everlights6_RGB.postUpdate(HSBType.fromRGB(0, 0, 0))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
Everlights_Chase.postUpdate(100)
} else if (Everlights_Preset.state == "Halloween") {
Everlights_Count.postUpdate(3)
Everlights1_RGB.postUpdate(HSBType.fromRGB(255, 41, 0))
Everlights2_RGB.postUpdate(HSBType.fromRGB(255, 41, 0))
Everlights3_RGB.postUpdate(HSBType.fromRGB(115, 0, 255))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
gEverlightsExclusive.sendCommand(0)
} else if (Everlights_Preset.state == "Thanksgiving") {
Everlights_Count.postUpdate(4)
Everlights1_RGB.postUpdate(HSBType.fromRGB(255, 135, 33))
Everlights2_RGB.postUpdate(HSBType.fromRGB(255, 75, 0))
Everlights3_RGB.postUpdate(HSBType.fromRGB(153, 0, 0))
Everlights4_RGB.postUpdate(HSBType.fromRGB(255, 75, 0))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
gEverlightsExclusive.sendCommand(0)
} else if (Everlights_Preset.state == "Christmas") {
Everlights_Count.postUpdate(4)
Everlights1_RGB.postUpdate(HSBType.fromRGB(255, 255, 255))
Everlights2_RGB.postUpdate(HSBType.fromRGB(255, 0, 0))
Everlights3_RGB.postUpdate(HSBType.fromRGB(255, 255, 255))
Everlights4_RGB.postUpdate(HSBType.fromRGB(0, 176, 0))
Everlights_Fade.postUpdate(0)
Everlights_Blink.postUpdate(0)
Everlights_Strobe.postUpdate(0)
gEverlightsExclusive.sendCommand(0)
}
EverlightsZone1_Data.postUpdate(executeCommandLine("/var/lib/openhab2/everlights.rb", 500))
EverlightsZone2_Data.postUpdate(executeCommandLine("/var/lib/openhab2/everlights.rb --reverse", 500))
Everlights_PatternChanging.postUpdate(OFF)
end
Switch item=Everlights_Switch
Text label="Everlights Pattern" {
Frame label="Presets" {
Selection item=Everlights_Preset mappings=[
""="---",
"Valentine's Day"="Valentine's Day",
"St. Patrick's Day"="St. Patrick's Day",
"Easter"="Easter",
"Patriotic"="Patriotic",
"Halloween"="Halloween",
"Thanksgiving"="Thanksgiving",
"Christmas"="Christmas"]
}
Frame label="Colors" {
Selection item=Everlights_Count mappings=[1="1", 2="2", 3="3", 4="4", 5="5", 6="6", 7="7", 8="8", 9="9", 10="10"]
Colorpicker item=Everlights1_RGB visibility=[Everlights_Count>=1]
Colorpicker item=Everlights2_RGB visibility=[Everlights_Count>=2]
Colorpicker item=Everlights3_RGB visibility=[Everlights_Count>=3]
Colorpicker item=Everlights4_RGB visibility=[Everlights_Count>=4]
Colorpicker item=Everlights5_RGB visibility=[Everlights_Count>=5]
Colorpicker item=Everlights6_RGB visibility=[Everlights_Count>=6]
Colorpicker item=Everlights7_RGB visibility=[Everlights_Count>=7]
Colorpicker item=Everlights8_RGB visibility=[Everlights_Count>=8]
Colorpicker item=Everlights9_RGB visibility=[Everlights_Count>=9]
Colorpicker item=Everlights10_RGB visibility=[Everlights_Count>=10]
}
Frame label="Mode" {
Slider item=Everlights_Twinkle
Slider item=Everlights_Fade
Slider item=Everlights_Blink
Slider item=Everlights_Strobe
Slider item=Everlights_Chase
Slider item=Everlights_ChaseReverse
Slider item=Everlights_Wipe
Slider item=Everlights_Random
}
}
#!/usr/bin/env ruby
require 'cgi'
require 'holidays'
require 'json'
# keys are either a single value, or an array that's a range of two date_specs
# for values, it's the pattern name. if it's nil, match the holiday's name
# for date_specs, it can be a month and day, or it can be a holiday name.
# you can append + or - a number to the end of the string to do lights "around"
# a date
SCHEDULE = {
"Valentine's Day" => nil,
"St. Patrick's Day" => nil,
["Easter Sunday-1", "Easter Sunday"] => "Easter",
"Memorial Day" => "Patriotic",
["Independence Day-3", "Independence Day"] => "Patriotic",
["Jul 24-1", "Jul 24"] => "Patriotic",
["Halloween-7", "Halloween"] => nil,
["Thanksgiving-4", "Thanksgiving"] => nil,
["Thanksgiving+1", "Dec 31"] => "Christmas",
# can't go over the year boundary
"Jan 1" => "Christmas",
}
HOLIDAYS = Holidays.year_holidays([:us, :informal], Date.parse('Jan 1')).map { |x| [x[:name], x[:date]] }.to_h
def normalize_schedule(schedule)
schedule.map do |(dates, pattern)|
dates = [dates, dates] unless dates.is_a?(Array)
pattern ||= dates.last
[dates, pattern]
end.to_h
end
def identify_date(date_spec)
offset = 0
if date_spec =~ /^(.+)([+-]\d+)$/
offset = $2.to_i
date_spec = $1
end
date = HOLIDAYS[date_spec] || Date.parse(date_spec)
date + offset
end
def dates_range(date_specs)
identify_date(date_specs.first)..identify_date(date_specs.last)
end
def search_schedule(date)
normalize_schedule(SCHEDULE).find { |(date_specs, pattern)| dates_range(date_specs).include?(date) }&.last
end
date = ARGV[0] ? Date.parse(ARGV[0]) : Date.today
puts search_schedule(date)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment