-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is also done and no changes have been done for a while, except for some finishing, so it's a good time to release it
- Loading branch information
Showing
3 changed files
with
234 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
--- | ||
layout: default | ||
title: CheerLights Live | ||
description: A simple viewer/client for CheerLights with extensive code documentation for educational purposes. | ||
--- | ||
|
||
<!-- Welcome! This is the source code of the page of "CheerLights Live". --> | ||
|
||
<!-- Some Day.js libraries to handle dates --> | ||
<script defer src="https://cdn.jsdelivr.net/combine/npm/dayjs@1,npm/dayjs@1/plugin/relativeTime.min.js,npm/dayjs@1/plugin/customParseFormat.min.js,npm/dayjs@1/plugin/utc.min.js,npm/dayjs@1/plugin/timezone.min.js"></script> | ||
|
||
<!-- Includes the script that makes this page possible. | ||
This file can be found beside this index.html page. --> | ||
<script defer src="script.js"></script> | ||
|
||
<!-- Some styles regarding the page --> | ||
<style> | ||
/* The light element. */ | ||
#light { | ||
/* We will use the variable --cl-main so we don't need to | ||
duplicate the colors for multiple parts of the stylesheet. */ | ||
background: var(--cl-main); | ||
border-radius: 50%; | ||
height: 10rem; | ||
width: 10rem; | ||
border: 1px solid var(--cl-secondary); | ||
|
||
box-shadow: 0 0 0 0 var(--cl-main); | ||
/* transform: scale(1); */ | ||
/* animation: pulse 2s; */ | ||
} | ||
|
||
/* An animation defining the pulse. */ | ||
@keyframes pulse { | ||
0% { | ||
/* transform: scale(0.95); */ | ||
box-shadow: 0 0 0 0 var(--cl-secondary); | ||
} | ||
|
||
70% { | ||
/* transform: scale(1); */ | ||
box-shadow: 0 0 0 10px rgba(255, 255, 255, 1); | ||
} | ||
|
||
100% { | ||
/* transform: scale(0.95); */ | ||
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); | ||
} | ||
} | ||
</style> | ||
|
||
<div class="container"> | ||
<section class="row"> | ||
<div class="col d-flex flex-column justify-content-center"> | ||
<p class="h2">The current color is <span class="light-text font-weight-bold"></span>.</p> | ||
<div> | ||
<div class="d-flex justify-content-between"> | ||
<div class="mr-1">Event: </div> | ||
<div class="text-right d-flex justify-content-right"> | ||
<div class="info-event-2 mb-0 mr-2"></div> | ||
<div class="info-event mb-0 text-monospace"></div> | ||
</div> | ||
</div> | ||
</div> | ||
<div> | ||
<div class="d-flex justify-content-between"> | ||
<div class="mr-1">Received: </div> | ||
<div class="text-right d-flex justify-content-right"> | ||
<div class="info-received-2 mb-0 mr-2"></div> | ||
<div class="info-received mb-0 text-monospace"></div> | ||
</div> | ||
</div> | ||
</div> | ||
<div> | ||
<div class="d-flex justify-content-between"> | ||
<div class="mr-1">Fetch: </div> | ||
<div class="text-right d-flex justify-content-right"> | ||
<div class="info-fetch-2 mb-0 mr-2"></div> | ||
<div class="info-fetch mb-0 text-monospace"></div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
</div> | ||
<div class="col-auto"> | ||
<div id="light"></div> | ||
</div> | ||
</section> | ||
<section id="status"> | ||
<p style="text-align: center; width: 100%; margin-bottom: 0"> </p> | ||
<div class="progress" style="width: 100%"> | ||
<div class="progress-bar" role="progressbar" style="width: 0"></div> | ||
</div> | ||
</section> | ||
<p></p> | ||
<section class="jumbotron py-5"> | ||
<h2 class="mb-4">About <small class="text-muted">CheerLights Live</small></h2> | ||
<p>This page displays the current light on <a href="https://cheerlights.com/">CheerLights</a>, a public light service/"global network of synchronized lights" that everyone can control. You are a part of "the network" by opening this page, and you can control it.</p> | ||
<p>To control the light, simply interact with the bot present on <a href="https://cheerlights.com/discord">the CheerLights Discord server</a>, or mentioning the account on Mastodon, <a href="https://botsin.space/@CheerLights">@[email protected]</a>. The list of colors are present on <a href="https://cheerlights.com/learn/">their website</a>.</p> | ||
<p class="mb-0">This page also serves an educational purpose due to how simple it is (get the color from the API, show the color on the page), has a substantial amount of documentation, and the usage of a public, active API. The source code can be viewed on <a href="https://github.com/Hans5958/mini-htmls/tree/master/site/cheerlights-live">the GitHub repository</a>.</p> | ||
</section> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// This file serves as the script that makes the page interactive. | ||
// (basically making the whole page possible) | ||
// Note that the usage of "date" has the same meaning as the Date object in | ||
// JavaScript, which also includes the time. If you care only the light, | ||
// the parts regarding dates can be safetly ignored. | ||
|
||
/** | ||
* Formats the date into a nice string. | ||
* @param day A Day.js object, which includes a date. | ||
* @returns A nice string displaying the date. | ||
*/ | ||
const dayToString1 = day => { | ||
return day.format('DD/MM/YYYY HH:mm:ss Z') | ||
} | ||
|
||
/** | ||
* The element for the light. | ||
*/ | ||
const lightEl = document.querySelector('#light') | ||
|
||
/** | ||
* The current event ID. | ||
*/ | ||
let currentId = null | ||
|
||
/** | ||
* The date the current event is receieved. | ||
*/ | ||
let receivedDate = null | ||
|
||
/** | ||
* Fetches the current event of the light and updates the page with new information. | ||
*/ | ||
const execute = async () => { | ||
// Resets the animation | ||
lightEl.style.setProperty('animation', 'none') | ||
|
||
// Fetches the API related to Cheerlights. The API can be found on their site. | ||
// The fetch() function returns a Promise (an type of asynchronous function), which we need to wait with await. | ||
const request = await fetch('http://api.thingspeak.com/channels/1417/field/1/last.json') | ||
|
||
// Tells that the result is a JSON, in which should be parsed | ||
// This, too, returns a Promise, which we need to wait with the same way. | ||
const data = await request.json() | ||
|
||
// Set the color variable for ease of access | ||
const color = data.field1 | ||
|
||
// Put a pulsing animation | ||
lightEl.style.setProperty('animation', 'pulse 2s') | ||
|
||
// Set few relevant dates | ||
|
||
/** | ||
* The date included on the data. | ||
*/ | ||
const eventDate = dayjs(data.created_at, 'YYYY-MM-DDTHH:mm:ssWIB') | ||
|
||
/** | ||
* The date on which the data is fetched. | ||
*/ | ||
const fetchDate = dayjs() | ||
|
||
// Updates the dates on the document | ||
updateTextContent('.info-event', dayToString1(eventDate)) | ||
if (receivedDate) updateTextContent('.info-received', dayToString1(receivedDate)) | ||
updateTextContent('.info-fetch', dayToString1(fetchDate)) | ||
|
||
// Run these next functions only when it is a new event, inferred from the ID. | ||
// Ideally we don't want to redo something that has been done because it waste | ||
// resources (in this case, negligble), but this also handles the part related | ||
// the date on receivedDate. If you care only the light, this can also be | ||
// safetly ignored. | ||
if (currentId === data.entry_id) return | ||
currentId = data.entry_id | ||
|
||
// Set receivedDate and update the date on the document | ||
receivedDate = dayjs() | ||
updateTextContent('.info-received', dayToString1(receivedDate)) | ||
|
||
// Update the displayed colors | ||
lightEl.style.setProperty('--cl-main', color) | ||
updateTextContent('.light-text', color) | ||
// Sets the secondary color, this mainly to handle an edge case when it is | ||
// white, otherwise you can see anything. If it is any other color, the | ||
// secondary color would be the same as the main one. | ||
if (color === 'white') { | ||
lightEl.style.setProperty('--cl-secondary', 'black') | ||
} else { | ||
lightEl.style.setProperty('--cl-secondary', color) | ||
} | ||
} | ||
|
||
execute() | ||
|
||
let statusProgressBar = 'fetch' | ||
|
||
/** | ||
* Handles the timer when it is updating | ||
*/ | ||
const executeTimer = async () => { | ||
statusProgressBar = "fetch" | ||
document.querySelector("#status .progress-bar").style.width = "0" | ||
document.querySelector("#status .progress-bar").style.transition = "width 0.25s ease" | ||
updateTextContent("#status p", `Fetching data...`) | ||
await execute() | ||
document.querySelector("#status .progress-bar").style.width = "100%" | ||
updateTextContent("#status p", "Data fetched!") | ||
setTimeout(() => { | ||
document.querySelector("#status .progress-bar").style.transition = "unset" | ||
statusProgressBar = "timer" | ||
}, 1000) | ||
} | ||
|
||
/** | ||
* Handles the countdown of the timer | ||
* @param {*} timer | ||
*/ | ||
const executeTick = timer => { | ||
if (statusProgressBar === "timer") { | ||
updateTextContent("#status p", `Waiting... (${timer.toFixed(2)}s)`) | ||
document.querySelector("#status .progress-bar").style.width = `${100 - (timer / 10) * 100}%` | ||
} | ||
} | ||
|
||
// This function can be found on site/assets/js/base.js. | ||
window.setIntervalFancy(executeTick, executeTimer, 5000, true) |