From 03716d5b80cee614ab26edffc829e8d9df9cd8f3 Mon Sep 17 00:00:00 2001 From: adrian w kirk Date: Sat, 17 Jul 2021 21:04:04 +0100 Subject: [PATCH] solar clock: First Commit --- apps.json | 25 ++ apps/solarclock/ChangeLog | 1 + apps/solarclock/README.md | 1 + apps/solarclock/app.png | Bin 0 -> 33522 bytes apps/solarclock/solar_clock-icon.js | 1 + apps/solarclock/solar_clock.js | 466 +++++++++++++++++++++++++ apps/solarclock/solar_clock.png | Bin 0 -> 2669 bytes apps/solarclock/solar_colors.js | 14 + apps/solarclock/solar_controller.js | 247 +++++++++++++ apps/solarclock/solar_date_utils.js | 132 +++++++ apps/solarclock/solar_graphic_utils.js | 94 +++++ apps/solarclock/solar_loc.Iceland.json | 4 + apps/solarclock/solar_loc.Kauai.json | 4 + apps/solarclock/solar_loc.Tokyo.json | 4 + apps/solarclock/solar_loc.local.json | 3 + apps/solarclock/solar_location.js | 84 +++++ apps/solarclock/solar_locations.json | 1 + apps/solarclock/solar_math_utils.js | 34 ++ 18 files changed, 1115 insertions(+) create mode 100644 apps/solarclock/ChangeLog create mode 100644 apps/solarclock/README.md create mode 100644 apps/solarclock/app.png create mode 100644 apps/solarclock/solar_clock-icon.js create mode 100644 apps/solarclock/solar_clock.js create mode 100644 apps/solarclock/solar_clock.png create mode 100644 apps/solarclock/solar_colors.js create mode 100644 apps/solarclock/solar_controller.js create mode 100644 apps/solarclock/solar_date_utils.js create mode 100644 apps/solarclock/solar_graphic_utils.js create mode 100644 apps/solarclock/solar_loc.Iceland.json create mode 100644 apps/solarclock/solar_loc.Kauai.json create mode 100644 apps/solarclock/solar_loc.Tokyo.json create mode 100644 apps/solarclock/solar_loc.local.json create mode 100644 apps/solarclock/solar_location.js create mode 100644 apps/solarclock/solar_locations.json create mode 100644 apps/solarclock/solar_math_utils.js diff --git a/apps.json b/apps.json index 80dc1e2b1..b6192b2e1 100644 --- a/apps.json +++ b/apps.json @@ -265,6 +265,31 @@ {"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"} ] }, + { "id": "solarclock", + "name": "Solar Clock", + "icon": "solar_clock.png", + "version":"0.01", + "description": "The solar clock will use your current or chosen location to work out the the current sun phase of the day", + "tags": "clock", + "type":"clock", + "allow_emulator":false, + "readme": "README.md", + "storage": [ + {"name":"solarclock.app.js","url":"solar_clock.js"}, + {"name":"solarclock.img","url":"solar_clock-icon.js","evaluate":true}, + {"name":"solar_colors.js","url":"solar_colors.js"}, + {"name":"solar_controller.js","url":"solar_controller.js"}, + {"name":"solar_date_utils.js","url":"solar_date_utils.js"}, + {"name":"solar_graphic_utils.js","url":"solar_graphic_utils.js"}, + {"name":"solar_location.js","url":"solar_location.js"}, + {"name":"solar_math_utils.js","url":"solar_math_utils.js"}, + {"name":"solar_loc.Iceland.json","url":"solar_loc.Iceland.json"}, + {"name":"solar_loc.Kauai.json","url":"solar_loc.Kauai.json"}, + {"name":"solar_loc.Tokyo.json","url":"solar_loc.Tokyo.json"}, + {"name":"solar_loc.local.json","url":"solar_loc.local.json"} + {"name":"solar_locations.json","url":"solar_locations.json"} + ] + }, { "id": "sweepclock", "name": "Sweep Clock", "icon": "sweepclock.png", diff --git a/apps/solarclock/ChangeLog b/apps/solarclock/ChangeLog new file mode 100644 index 000000000..d53df991b --- /dev/null +++ b/apps/solarclock/ChangeLog @@ -0,0 +1 @@ +0.01: Initial Release diff --git a/apps/solarclock/README.md b/apps/solarclock/README.md new file mode 100644 index 000000000..51c95139b --- /dev/null +++ b/apps/solarclock/README.md @@ -0,0 +1 @@ +# Solar Clock diff --git a/apps/solarclock/app.png b/apps/solarclock/app.png new file mode 100644 index 0000000000000000000000000000000000000000..763c00b1de9f5c73ca4af264c6d65cc5eb08c090 GIT binary patch literal 33522 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Ν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 literal 0 HcmV?d00001 diff --git a/apps/solarclock/solar_clock-icon.js b/apps/solarclock/solar_clock-icon.js new file mode 100644 index 000000000..d9bdd8c65 --- /dev/null +++ b/apps/solarclock/solar_clock-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("lEowkA/4AGmYIHABHzmVCCaE0kUin4TPmUimQTQ+UzmcvJ6EjCaP/kYABCaEymYTl+Q7SMgITTmQTQPAK0RMgITm+QTS+ciPCcikQpPY4MjmYTO+czmcyHh4TCmcvJ54nCPCBjBJx4oECc8zJ6ATTn48RE4YTTHh4SDH4ImRFBwTGFBgTGFBgSGFBYmHUgITRmcyFBASFAoUjE5PzkQLBHJxiDAQP/GxAA==")) diff --git a/apps/solarclock/solar_clock.js b/apps/solarclock/solar_clock.js new file mode 100644 index 000000000..d1154e110 --- /dev/null +++ b/apps/solarclock/solar_clock.js @@ -0,0 +1,466 @@ +const DateUtils = require("solar_date_utils.js"); +const Math2 = require("solar_math_utils.js"); +const GraphicUtils = require("solar_graphic_utils.js"); +const Colors = require("solar_colors.js"); +const LocationUtils = require("solar_location.js"); + +var screen_info = { + screen_width : g.getWidth(), + screen_start_x : 0, + screen_centre_x: g.getWidth()/2, + screen_height : (g.getHeight()-100), + screen_start_y : 100, + screen_centre_y : 90 + (g.getHeight()-100)/2, + screen_bg_color : Colors.BLACK, + sun_radius: 8, + sun_x : null, + sun_y : null, + sunrise_y : null, +} +const img_width=40; +const img_height=30; +var img_buffer = Graphics.createArrayBuffer(img_width,img_height,8); +var img = {width:img_width,height:img_height,bpp:8,transparent:0,buffer:img_buffer.buffer}; +var img_info = { + x: null, + y: null, + img: img, + img_buffer: img_buffer +} +const COSINE_COLOUR= Colors.GREY; +const HORIZON_COLOUR = Colors.GREY; +const SolarController = require("solar_controller.js"); +var controller = new SolarController(); +var curr_mode = null; +var last_sun_draw_time = null; +var draw_full_cosine = true; + +function draw_sun(now, day_info) { + + var now_fraction = (now.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS; + var now_x = now_fraction * screen_info.screen_width; + if(screen_info.sun_x != null && Math.abs(now_x- screen_info.sun_x) < 1){ + console.log("no sun movement"); + return false; + } + // now calculate thew new sun coordinates + var now_radians = Math2.TWO_PI *(now_x - screen_info.screen_centre_x)/screen_info.screen_width; + var now_y = screen_info.screen_centre_y - (screen_info.screen_height * Math.cos(now_radians) / 2); + if(Math.abs(now_x - screen_info.sun_x) > 5){ + clear_sun(); + } + // update the screen info with the new sun info + screen_info.sun_x = now_x; + screen_info.sun_y = now_y; + last_sun_draw_time = now; + + if(draw_full_cosine){ + //console.log("drawing full cosine"); + GraphicUtils.draw_cosine(screen_info.screen_start_x, + screen_info.screen_width, + COSINE_COLOUR, + screen_info); + draw_full_cosine = false; + } + if(curr_mode == null) { + GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info); + } + // decide on the new sun drawing mode and draw + curr_mode = controller.mode(now,day_info,screen_info); + img_info.img_buffer.clear(); + img_info.img_buffer.setColor(screen_info.screen_bg_color[0], + screen_info.screen_bg_color[1], + screen_info.screen_bg_color[2], + ); + img_info.img_buffer.fillRect(0,0,img_width, img_height); + img_info.x = screen_info.sun_x - img_info.img.width/2; + img_info.y = screen_info.sun_y - img_info.img.height/2; + + var cosine_dist = screen_info.sun_radius/Math.sqrt(2); + GraphicUtils.draw_cosine(img_info.x, + screen_info.sun_x - cosine_dist, + COSINE_COLOUR, + screen_info, + img_info); + GraphicUtils.draw_cosine(screen_info.sun_x + cosine_dist, + screen_info.sun_x + img_width, + COSINE_COLOUR, + screen_info, + img_info); + + curr_mode.draw(now,day_info,screen_info,img_info); + + var sunrise_dist = Math.abs(screen_info.sunrise_y-screen_info.sun_y); + if( sunrise_dist <= img_height) { + GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info,img_info); + } else if(sunrise_dist <= img_height*2.5) { + GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info); + } + // we draw a blank where the image is going to be drawn to clear out the area + g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]); + g.fillRect(img_info.x,img_info.y-2,img_info.x+img_width,img_info.y + img_height + 2); + g.drawImage(img,img_info.x,img_info.y); + // paint the cosine curve back to the normal color where it just came from + GraphicUtils.draw_cosine(img_info.x - 3, + img_info.x, + COSINE_COLOUR, + screen_info); + GraphicUtils.draw_cosine(img_info.x + img_width, + img_info.x + img_width + 3, + COSINE_COLOUR, + screen_info); + return true; +} + +function clear_sun(){ + g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]); + g.fillRect(img_info.x,img_info.y,img_info.x+img_width,img_info.y + img_width); + GraphicUtils.draw_cosine(img_info.x - 4, + img_info.x + img_width + 4, + COSINE_COLOUR, + screen_info); + GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info); + screen_info.sun_x = null; + screen_info.sun_y = null; +} + +var last_time = null; +var last_offset = null; +var last_date = null; + +const time_color = Colors.WHITE; +const date_color = Colors.YELLOW; +const DATE_Y_COORD = 35; +const DATE_X_COORD = 10; +const TIME_X_COORD = 140; +const TIME_Y_COORD = 35; +const OFFSET_Y_COORD = 70; +const LOCATION_Y_COORD = 55; + +function write_date(now){ + var new_date = require('locale').dow(now,1) + " " + Math2.format00(now.getDate()); + //console.log("writing date:" + new_date) + g.setFont("Vector",15); + g.setFontAlign(-1,-1,0); + if(last_date != null){ + if(new_date == last_date){ + return; + } + g.setColor(screen_info.screen_bg_color[0], + screen_info.screen_bg_color[1], + screen_info.screen_bg_color[2]); + g.drawString(last_date, DATE_X_COORD,DATE_Y_COORD); + } + g.setColor(date_color[0],date_color[1],date_color[2]); + g.drawString(new_date, DATE_X_COORD,DATE_Y_COORD); + last_date = new_date; +} + +var last_status_msg = "" +var last_gps_coords_msg_n = ""; +var last_gps_coords_msg_e = ""; +const GPS_MSG_X_COORD = 70; +const GPS_MSG_Y = 220; +const GPS_MSG_COORDS_Y_E = 80; +const GPS_MSG_COORDS_Y_N = 90; + +function write_GPS_status(){ + var gps_coords = location.getCoordinates(); + var gps_coords_msg_n; + var gps_coords_msg_e; + if(gps_coords != null){ + gps_coords_msg_n = "N: " + gps_coords[0]; + gps_coords_msg_n = gps_coords_msg_n.substr(0,Math.min(gps_coords_msg_n.length - 1,10)); + gps_coords_msg_e = "E: " + gps_coords[1]; + gps_coords_msg_e = gps_coords_msg_e.substr(0,Math.min(gps_coords_msg_e.length - 1,10)); + } else { + gps_coords_msg_n = ""; + gps_coords_msg_e = ""; + } + var status_msg = ""; + if(location.isGPSLocation()) { + if(gps_coords == null) { + if (location.getGPSPower() > 0) { + status_msg = "Finding GPS Position"; + } else { + status_msg = "ERROR GPS Position not found"; + } + } else { + if (location.getGPSPower() > 0) { + status_msg = "Updating GPS Position"; + } + } + } + g.setFont("Vector",11); + g.setFontAlign(-1,-1,0); + if(last_status_msg != status_msg) { + g.setColor(screen_info.screen_bg_color[0], + screen_info.screen_bg_color[1], + screen_info.screen_bg_color[2]); + g.drawString(last_status_msg, GPS_MSG_X_COORD, GPS_MSG_Y); + g.setColor(Colors.YELLOW[0],Colors.YELLOW[1],Colors.YELLOW[2]); + g.drawString(status_msg, GPS_MSG_X_COORD, GPS_MSG_Y); + last_status_msg = status_msg; + } + + if(last_gps_coords_msg_e != gps_coords_msg_e) { + g.setColor(screen_info.screen_bg_color[0], + screen_info.screen_bg_color[1], + screen_info.screen_bg_color[2]); + g.drawString(last_gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E); + g.drawString(last_gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N); + g.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]); + + g.drawString(gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E); + g.drawString(gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N); + last_gps_coords_msg_e = gps_coords_msg_e; + last_gps_coords_msg_n = gps_coords_msg_n; + } +} + +function write_time(now){ + var new_time = format_time(now); + g.setFont("Vector",35); + g.setFontAlign(-1,-1,0); + if(last_time != null){ + g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]); + g.drawString(last_time, TIME_X_COORD,TIME_Y_COORD); + } + g.setColor(time_color[0],time_color[1],time_color[2]); + g.drawString(new_time, TIME_X_COORD,TIME_Y_COORD); + last_time = new_time; +} + +function format_time(now){ + var time = new Date(now.getTime() - time_offset); + var hours = time.getHours() % 12; + if(hours < 1){ + hours = 12; + } + return Math2.format00(hours) + ":" + Math2.format00(time.getMinutes()); +} + +function write_offset(){ + var new_offset = format_offset(); + g.setFont("Vector",15); + g.setFontAlign(-1,-1,0); + if(last_offset != null){ + g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]); + g.drawString(last_offset, TIME_X_COORD,OFFSET_Y_COORD); + } + g.setColor(time_color[0],time_color[1],time_color[2]); + g.drawString(new_offset, TIME_X_COORD,OFFSET_Y_COORD); + last_offset = new_offset; +} + +function format_offset(){ + if(time_offset == 0) + return ""; + + var hours_offset = Math.abs(time_offset) / DateUtils.HOUR_MILLIS; + var mins_offset = Math.abs(time_offset) / DateUtils.MIN_MILLIS; + var mins_offset_from_hour = mins_offset % 60; + //console.log("mins offset=" + mins_offset + " mins_offset_from_hour=" + mins_offset_from_hour); + var sign = "+"; + if(time_offset < 0) + sign = "-"; + + return sign + Math2.format00(hours_offset) + ":" + Math2.format00(mins_offset_from_hour); +} + +let time_offset = 0; +let last_draw_time = null; +var day_info = null; +var location = LocationUtils.load_locations(); +var last_location_name = null; + +function write_location_name() { + var new_location_name = location.getName(); + g.setFont("Vector", 20); + g.setFontAlign(-1, -1, 0); + if (last_location_name != null) { + g.setColor(screen_info.screen_bg_color[0], screen_info.screen_bg_color[1], screen_info.screen_bg_color[2]); + g.drawString(last_location_name, DATE_X_COORD, LOCATION_Y_COORD); + } + g.setColor(time_color[0], time_color[1], time_color[2]); + if (new_location_name != "local") { + g.drawString(new_location_name, DATE_X_COORD, LOCATION_Y_COORD); + } + last_location_name = new_location_name; +} + +location.addUpdateListener( + (loc)=>{ + console.log("location update:" + JSON.stringify(loc)); + clear_sun(); + GraphicUtils.draw_sunrise_line(screen_info.screen_bg_color, day_info, screen_info); + day_info = null; + screen_info.sunrise_y = null; + curr_mode = null; + draw_clock(); + } +); + + + +function dayInfo(now) { + if (day_info == null || now > day_info.day_end) { + var coords = location.getCoordinates(); + if(coords != null) { + day_info = DateUtils.sunrise_sunset(now, coords[0], coords[1], location.getUTCOffset()); + //console.log("day info:" + JSON.stringify(day_info)); + } else { + day_info = null; + } + } + return day_info; +} + +function time_now() { + var timezone_offset_hours = location.getUTCOffset(); + if(timezone_offset_hours != null) { + var local_offset_hours = -new Date().getTimezoneOffset()/60; + var timezone_offset_millis = + (timezone_offset_hours - local_offset_hours) * DateUtils.HOUR_MILLIS; + return new Date(Date.now() + time_offset + timezone_offset_millis); + } else { + return new Date(Date.now() + time_offset); + } +} + + +function draw_clock(){ + var start_time = Date.now(); + var now = time_now(); + + var day_info = dayInfo(now); + if(day_info != null) { + draw_sun(now, day_info); + } + write_time(now); + write_date(now); + write_offset(); + write_location_name(); + write_GPS_status(); + last_draw_time = now; + log_memory_used(); + var time_taken = Date.now() - start_time; + console.log("drawing clock:" + now.toISOString() + " time taken:" + time_taken ); +} + +function log_memory_used() { + var memory = process.memory(); + console.log("memory used:" + memory.usage + + " total:" + memory.total + "->" + + " ->" + memory.usage/memory.total + ); +} + +function button1pressed(){ + console.log("button 1 pressed"); + time_offset = 0; + clear_sun(); + day_info = null; + draw_clock(); +} + +function button3pressed(){ + console.log("button 3 pressed"); + time_offset = 0; + location.nextLocation(); +} + +function button4pressed(){ + time_offset -= DateUtils.HOUR_MILLIS/4; + draw_clock(); + setTimeout(()=>{ + if(BTN4.read()){ + button4pressed(); + } + }, + 50 + ) +} + +function button5pressed(){ + time_offset += DateUtils.HOUR_MILLIS/4; + draw_clock(); + setTimeout(()=>{ + if(BTN5.read()){ + button5pressed(); + } + }, + 50 + ) +} + +// The interval reference for updating the clock +let interval_ref = null; +function clear_timers(){ + if(interval_ref != null) { + clearInterval(interval_ref); + interval_ref = null; + } +} + +function start_timers(){ + var date = new Date(); + var secs = date.getSeconds(); + var nextMinuteStart = 60 - secs; + setTimeout(schedule_draw_clock,nextMinuteStart * 1000); + draw_clock(); +} +function schedule_draw_clock(){ + clear_timers(); + if (Bangle.isLCDOn()) { + interval_ref = setInterval(() => { + if (!Bangle.isLCDOn()) { + console.log("draw clock callback - skipped redraw"); + } else { + draw_clock(); + } + }, DateUtils.MIN_MILLIS + ); + draw_clock(); + } else { + console.log("scheduleDrawClock - skipped not visible"); + } +} + +Bangle.on('lcdPower', (on) => { + if (on) { + console.log("lcdPower: on"); + draw_clock(); + start_timers(); + } else { + console.log("lcdPower: off"); + clear_timers(); + } +}); + +Bangle.on('faceUp',function(up){ + if (up && !Bangle.isLCDOn()) { + clear_timers(); + Bangle.setLCDPower(true); + } +}); + +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +start_timers(); + +function button2pressed(){ + controller = null; + + location.shutdown(); + location = null; + + Bangle.showLauncher(); +} +setWatch(button2pressed, BTN2,{repeat:false,edge:"falling"}); +setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"}); +setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"}); +setWatch(button4pressed, BTN4,{repeat:true,edge:"rising"}); +setWatch(button5pressed, BTN5,{repeat:true,edge:"rising"}); \ No newline at end of file diff --git a/apps/solarclock/solar_clock.png b/apps/solarclock/solar_clock.png new file mode 100644 index 0000000000000000000000000000000000000000..70a1cd5327a61bc1b0d393b57e2093864e565b9c GIT binary patch literal 2669 zcmZ`*c{r478-GXk?p)c+{?6b0`1>Ob#g46*ZED8Wt*`}}=0PxoWfVXY{ zfXV;>X=+ZL9h&W6k_cEY8yi5At@8ssAaP(DTLZB_03-)+qOmo=5+wg!$AdJ!VYmPw zmJD!z!yIKx&az-{4rfbp<#BySXyk4Cp>v@3B_kEtLV#-FLzzm!eb76qq9$j;1di66$OI@1_nX{wV@Q+aTr`* zUmvE0fFTeNHUdIF?aOdyLVW4Fwu1aG4u(Ycq>-r%GR0S!6W85?;?FPygE@)5udO;6 zWUn8YeCglXVmAomcwlg-7VK9v5|jKdG>&JB_O-9AbZAa6)FB#~#4eaqmJuBNHNpSc zew4R`$CKz3Z+{Nl!I#W1LVTnA!~7GAlN)7AW|F*}Fk~N+FP)PKp`(R{{nGPGCgES1 zKY9MiG^2P^X!cZhPZFoGugQKgeM6&RoLT%do2|irwX!E~B#4H6zePrZBc{Isgchv(`i zxF%HKsg(i4gtInws%mGY)4<0qdZJy{N_E7pmFcU7E18)6HqG*yZ+zjjWgs#JK606g|s|TG?#Q19D1d+;5eF>Pv+vGdCU{;UUV% z)SZb^S-z<8dz!4|ec{%&p7~&IJxt=e2b4mx$G}`3-T%5P@!`tVD`}u}N>Sk{-Rt1Q z!!aX<8{mT3h0eTjJtEv$uCo(R>q2-I%nJ*)o2|5lgRwVs7kD4<(WW`yN8sUzdk-Qz z9)Fa`J`ywQyp5;d)=^#m|4q?xxQtWnX4Xu_uV}=%0G5y*N zf>Z>umgyU2bUdl*BNrljje9C$mFv;tbSXB)Vca$j}6@(Skd7K&84YGW61+)W)(% zuUX+kN}+^%+P0W6Jl0xgy_k0Z{}ck!;DXiExZRMzpmeQUXEE(li*bm zDGjsY4>G++N#^eG1-|)1!)>Nm(CDX^Kr2j7W(jBHHD_lW5fz4TyR1akS3c`u97cxyvnhGZ&z*n z! zB|H4m)x7F&j6->kCwk$P%j<@EF_S+ZKMIOQ8WZE}2VM~(-PI-eBd7d3L)x!q60k{j zs1V^jg~bkr(k23TDl?0U)K+jJ0`7@jZ3EAaEvjDHf8_9gzz~ybepqXDbWG#XkixdL z?ws|d8D~JPQc2?d4Lc8bqLxe|LHXhnjM?r^2FS&+&OlVA%Hd89t3pT0rc&O48^P19 z2i_+6setc2em}G~^ggMi!xm*BK5E_=?mD7EIKTHdKPe%)xY3a(>Zn{LM5ge`$%L7-nhS`xJ~7Z(*1h2WfX^!9!|e>b(_N>%l46OF^w2ee>-7Aa>hk3bd_I7B`pp|q<8Ds0JZ#^k_^x03vT^>KzyIA6-Gin$_mX}?-;uCu}@oDBVkIq zYiE(YT|`4xZ{E=ov+AV*O`{33eO%d+hrHU42)`mjy~}ZlPnCjlQN8hFE9N5UtH7>_ z#nEdAbE?m4LRQ{y(i{BMGpAgN`X_Jpwdsa#%CoAg%POjPwSC7IW+b2mmmZ&Nu+_(Y zZf$R`)l_vKo~^Gb3+0Op5lrggzp}9*%6NS`Vmn5qIVlr)cVh(Pb1~G6-=eQEpgwYG zwwIsxQcY9SfyL*B3PT|~tK@7<@7&(!=<8#JO&t%iDD*C{$Eu!VX+rZYGz05uRM!yd guW3u`ALm1XW#=8{i!Y day_info.sunset_date){ + return SUNSET_COLOUR; + } else if(now < day_info.sunrise_date){ + return SUNRISE_COLOUR; + } else if(now < day_info.solar_noon) { + var sunrise_fraction = (day_info.sunrise_date - day_info.day_start) / DateUtils.DAY_MILLIS; + var rise_to_midday_fraction = (now_fraction_of_day - sunrise_fraction) / (0.5 - sunrise_fraction); + return Math2.interpolate(SUNRISE_COLOUR, MIDDAY_COLOUR, rise_to_midday_fraction); + } else { + var sunset_fraction = (day_info.day_end - day_info.sunset_date) / DateUtils.DAY_MILLIS; + var midday_to_sunset_fraction = (now_fraction_of_day - 0.5)/(0.5 - sunset_fraction); + //console.log("sunset_fraction=" + sunset_fraction + " midday_to_sunset_fraction=" + midday_to_sunset_fraction) + return Math2.interpolate(MIDDAY_COLOUR,SUNSET_COLOUR,midday_to_sunset_fraction); + } +} + +function draw_night_sun(sun_x,sun_y,sun_radius,img_info){ + var draw_info = GraphicUtils.draw_info(img_info); + draw_info.buff.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]); + draw_info.buff.fillCircle(sun_x - draw_info.offset_x, + sun_y - draw_info.offset_y, + sun_radius); + draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]); + draw_info.buff.fillCircle(sun_x - draw_info.offset_x, + sun_y - draw_info.offset_y, + sun_radius-1); +} + +function draw_partial_sun(time, day_info, screen_info,img_info){ + var sun_height = screen_info.sunrise_y - screen_info.sun_y; + if(sun_height > screen_info.sun_radius){ + var sun_color = daytime_sun_color(time,day_info); + var draw_info = GraphicUtils.draw_info(img_info); + draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]); + draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x, + screen_info.sun_y - draw_info.offset_y, + screen_info.sun_radius + ); + } else if(sun_height < -screen_info.sun_radius){ + draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info); + } else { + var draw_info = GraphicUtils.draw_info(img_info); + draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]); + draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x, + screen_info.sun_y - draw_info.offset_y, + screen_info.sun_radius-1); + var sun_color = daytime_sun_color(time,day_info); + draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]); + draw_info.buff.drawCircle(screen_info.sun_x - draw_info.offset_x, + screen_info.sun_y - draw_info.offset_y, + screen_info.sun_radius); + GraphicUtils.fill_circle_partial_y(screen_info.sun_x, + screen_info.sun_y, + screen_info.sun_radius, + screen_info.sun_y - screen_info.sun_radius, + screen_info.sunrise_y, + img_info + ); + } +} +function draw_random_background(screen_info, + img_info, + rgb_init, + rgb_step + ){ + var draw_info = GraphicUtils.draw_info(img_info); + var rgb = rgb_init; + var sky_to = Math.min(screen_info.sunrise_y-1, img_info.y + img_info.img.height - 3); + for(var sky_y=img_info.y+3;sky_y0) + rgb[i] = Math2.random_walk(rgb[i],rgb_step[i],1,0); + } + draw_info.buff.setColor(rgb[0],rgb[1],rgb[2]); + draw_info.buff.moveTo(screen_info.sun_x + + Math.random()*img_info.img.width/8 - + 0.4*img_info.img.width - + draw_info.offset_x, + sky_y - draw_info.offset_y); + draw_info.buff.lineTo(screen_info.sun_x + + 0.4*img_info.img.width - + Math.random()*img_info.img.width/8 - + draw_info.offset_x, + sky_y - draw_info.offset_y); + } + draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]); + draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x, + screen_info.sun_y - draw_info.offset_y, + screen_info.sun_radius+1); +} +class SolarMode { + test(time, day_info, screen_info){ throw "test undefined";} + draw(time, day_info, screen_info, img_buffer_info){ + throw "sun drawing undefined"; + } +} +class NightMode extends SolarMode { + toString(){return "NightMode";} + test(time, day_info, screen_info, img_info) { + return (time < day_info.sunrise_date || time > day_info.sunset_date); + } + draw(time, day_info, screen_info, img_info){ + draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info); + } +} +class DayLightMode extends SolarMode { + toString(){ + return "DayLightMode"; + } + test(time, day_info, screen_info){ + var sun_height = screen_info.sunrise_y - screen_info.sun_y; + /*console.log("DayLightMode " + + "time=" + time.toISOString() + + " sunset_date=" + day_info.sunset_date.toISOString() + + " sunrise_date=" + day_info.sunrise_date.toISOString() + );*/ + return time < day_info.sunset_date && + time > day_info.sunrise_date && + sun_height >= screen_info.sun_radius * 2 + SUNSET_START_HEIGHT; + } + _calc_corona_radius(now, day_info){ + if(now < day_info.sunset_date && + now > day_info.sunrise_date){ + var now_fraction_of_day =DateUtils.now_fraction_of_day(now,day_info); + var sunset_fraction = (day_info.sunset_date.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS; + var now_fraction_from_midday = + 1 - Math.abs(now_fraction_of_day-0.5)/(sunset_fraction-0.5); + return CORONA_RADIUS * now_fraction_from_midday; + } else { + return 0; + } + } + _drawCorona(corona_radius,sun_x,sun_y,sun_radius, draw_info){ + var thickness_rads = (Math2.TWO_PI/CORONA_LINES)/3; + var from_radius = sun_radius + CORONA_GAP; + if(corona_radius > from_radius + CORONA_MIN_LENGTH) { + for (var i = 0; i < CORONA_LINES; i++) { + var to_x1 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES + thickness_rads); + var to_y1 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES + thickness_rads); + var to_x2 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES - thickness_rads); + var to_y2 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES - thickness_rads); + var to_x3 = sun_x - draw_info.offset_x + corona_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES); + var to_y3 = sun_y - draw_info.offset_y + corona_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES); + draw_info.buff.fillPoly([to_x1, to_y1, to_x2, to_y2, to_x3, to_y3]); + } + } + } + draw(now, day_info, screen_info, img_info){ + var sun_color = daytime_sun_color(now,day_info); + var corona_radius = this._calc_corona_radius(now, day_info); + var draw_info = GraphicUtils.draw_info(img_info); + draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]); + if(corona_radius > screen_info.sun_radius){ + this._drawCorona(corona_radius, + screen_info.sun_x, + screen_info.sun_y, + screen_info.sun_radius, + draw_info); + } + draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x, + screen_info.sun_y - draw_info.offset_y, + screen_info.sun_radius); + } +} +class TwiLightMode extends SolarMode { + toString(){ + return "TwilightMode"; + } + test(time, day_info, screen_info){ + if(screen_info.sunrise_y == null) { + console.log("warning no sunrise_defined"); + return false; + } + var sun_height = screen_info.sunrise_y - screen_info.sun_y; + /*console.log("TwilightMode " + + "time=" + time.toISOString() + + " sun_height=" + sun_height + + " sun_radius=" + screen_info.sun_radius + );*/ + if(sun_height > -screen_info.sun_radius && + sun_height < screen_info.sun_radius * 2 + SUNSET_START_HEIGHT + ){ + //console.log("selected TwilightMode"); + return true; + } + return false; + } + draw(time, day_info, screen_info, img_info){ + if(time < day_info.solar_noon) { + draw_random_background(screen_info, + img_info, + [0,0.8,1], + [0.05,0.05,0.0]); + } else { + draw_random_background(screen_info, + img_info, + [1,0.75,Math.random()], + [0,0.05,0.05]); + } + draw_partial_sun(time,day_info,screen_info,img_info); + } +} +class SolarControllerImpl { + constructor(){ + this.solar_modes = [new TwiLightMode(), new DayLightMode()]; + this.default_mode = new NightMode(); + this.last = null; + } + toString(){ + return "SolarControllerImpl"; + } + mode(time, day_info, screen_info){ + if(this.last != null){ + if(this.last.test(time,day_info,screen_info)){ + return this.last; + } + } + for(var i=0; i" + solar_noon_datetime.toISOString()); + + var sunrise_time_LST = (solar_noon*1440-HA_sunrise_degrees*4)/1440; + var sunrise_time_LST_datetime = _to_time(now,sunrise_time_LST); + console.log("sunrise_time_LST=" + sunrise_time_LST + + "->" + sunrise_time_LST_datetime.toISOString()); + + var sunset_time_LST =(solar_noon*1440+HA_sunrise_degrees*4)/1440; + var sunset_time_LST_datetime = _to_time(now,sunset_time_LST); + console.log("sunset_time_LST=" + sunset_time_LST + + "->" + sunset_time_LST_datetime.toISOString()); + return { + day_start: new Date(solar_noon_datetime.getTime() - _DAY_MILLIS / 2), + sunrise_date: sunrise_time_LST_datetime, + //sunrise_fraction: sunrise_time_LST, + sunset_date: sunset_time_LST_datetime, + //sunset_fraction: sunset_time_LST, + solar_noon: solar_noon_datetime, + day_end: new Date(solar_noon_datetime.getTime() + _DAY_MILLIS / 2) + }; + }, + now_fraction_of_day: (now,day_info)=>{ + return (now.getTime() - day_info.day_start.getTime())/_DAY_MILLIS; + }, +} +module.exports = DateUtils; \ No newline at end of file diff --git a/apps/solarclock/solar_graphic_utils.js b/apps/solarclock/solar_graphic_utils.js new file mode 100644 index 000000000..3c2afec88 --- /dev/null +++ b/apps/solarclock/solar_graphic_utils.js @@ -0,0 +1,94 @@ +var DateUtils = require("solar_date_utils.js"); +var Math2 = require("solar_math_utils.js"); + +function _draw_info(img_info){ + if (img_info == null) { + return { + buff: g, + offset_x: 0, + offset_y: 0 + }; + } else { + return { + buff: img_info.img_buffer, + offset_x: img_info.x, + offset_y: img_info.y + }; + } + +} +const GraphicUtils = { + draw_info : (img_info)=>_draw_info(img_info), + draw_cosine : (from_x,to_x, line_colour, screen_info, img_info)=>{ + //console.log("draw_cosine from_x=" + from_x + " to_x=" + to_x); + var draw_info = _draw_info(img_info); + + draw_info.buff.reset(); + draw_info.buff.setColor(line_colour[0],line_colour[1],line_colour[2]); + first = true; + for(var x=from_x; x { + if (!this.in_use) + return; + + if (g.fix) { + var loc_info = { + last_update: new Date(), + coordinates: [g.lon, g.lat] + }; + console.log("Received gps fixing:" + JSON.stringify(loc_info)); + storage.writeJSON("solar_loc.local.json", this.location_info); + if(this.isGPSLocation()){ + this.location_info = loc_info; + this.notifyUpdate(); + } + this.setGPSPower(0); + } + }); + } + isGPSLocation(){return this.getName() == 'local';} + addUpdateListener(listener){this.listeners.push(listener);} + nextLocation() { + if(this.locations.length > 1) { + this.idx += 1; + this.idx = this.idx % this.locations.length; + console.log("location now:" + this.getName()); + this.init(); + this.notifyUpdate(); + } else { + console.log("no extra locations found"); + } + } + notifyUpdate(){ + for(var i=0; i{ + var locations = storage.readJSON(LOCATIONS_FILE); + console.log("loaded locations:" + locations); + var mgr = new LocationManager(locations); + mgr.init(); + return mgr; + } +} +module.exports = LocationUtils; \ No newline at end of file diff --git a/apps/solarclock/solar_locations.json b/apps/solarclock/solar_locations.json new file mode 100644 index 000000000..750b43206 --- /dev/null +++ b/apps/solarclock/solar_locations.json @@ -0,0 +1 @@ +["local","Tokyo","Iceland","Kauai"] \ No newline at end of file diff --git a/apps/solarclock/solar_math_utils.js b/apps/solarclock/solar_math_utils.js new file mode 100644 index 000000000..c88ba9961 --- /dev/null +++ b/apps/solarclock/solar_math_utils.js @@ -0,0 +1,34 @@ +const _TWO_PI = 2 * Math.PI; +const Maths2 = { + TWO_PI: _TWO_PI, + to_radians: (degrees)=> _TWO_PI * degrees / 360, + to_degrees: (radians)=> 360 * radians/ (_TWO_PI), + interpolate: (vector1, vector2, fraction)=>{ + var result = []; + for(var i=0; i< vector1.length; i++){ + var value = vector1[i] + (vector2[i] - vector1[i]) * fraction; + result.push(value); + } + return result; + }, + format00: (num)=>{ + var value = (num | 0); + if(value > 99 || value < 0) + throw "must be between in range 0-99"; + if(value < 10) + return "0" + value.toString(); + else + return value.toString(); + }, + random_walk: (value,step,max,min)=>{ + if(Math.random()>0.5){ + value -= step; + } else { + value += step; + } + value = Math.min(value,max); + value = Math.max(value,min); + return value; + } +} +module.exports = Maths2; \ No newline at end of file