trip5 / EspHome-Led-PixelClock

EHLPC: EspHome-Led-Clock (for MAX7219 Digit Display Clocks)
The Unlicense
14 stars 3 forks source link

Newbie walk through #1

Open drgkt opened 1 year ago

drgkt commented 1 year ago

Hi trip5 Thanks for the invite! Waiting for the unit to arrive, let's start with hardware and software required, as well as flashing wiring instructions.

drgkt commented 1 year ago

Hello. Have you forgotten me?

trip5 commented 1 year ago

Lol. Yes and no. Been working on an e-ink project lately.

I remember seeing this 1st message in an e-mail... and... you're still waiting on the clock right?

drgkt commented 1 year ago

Yes. Still waiting. Although recently one of my order got lost in transit and they did not reimburse me because they wanted me to provide proof I did not get the package!😡 Nevertheless, let's start with the items of the 1st post.

trip5 commented 1 year ago

Lol about the flashing part. It says in the readme. It's super-easy, just plug into your computer and press reset while holding the other button.

Do you have ESPHome already? Have you done any kind of flashing to smart home (ESP32 or ESP8266) chips before? Do you have any experience with Docker? or are you running Linux or Windows and that's what we'll need to flash with?

I need more information from you to judge where to start.

drgkt commented 1 year ago

Lol about the flashing part. It says in the readme. It's super-easy, just plug into your computer

You mean to use the USB cable that powers the clock and plug the other end into a USB port of the PC?

and press reset while holding the other button.

I assume there are 2 buttons on the clock, one says reset and the other says what?

Do you have ESPHome already?

No

Have you done any kind of flashing to smart home (ESP32 or ESP8266) chips before?

No

Do you have any experience with Docker?

No

or are you running Linux or Windows

Windows

and that's what we'll need to flash with?

I need more information from you to judge where to start.

Assume I know nothing. I can, however, follow step by step instructions.

trip5 commented 1 year ago

That's a good start. If you're interested in learning how to customize your clock fully, you should probably learn how to install Docker into Windows (this can be hard since it has a bunch of prerequisites).

https://docs.docker.com/desktop/install/windows-install/ - if you just start by downloading the install, it will generally tell you what you're missing and help you get them... Google can help with any issues... Between Docker and Windows System for Linux 2 this can take a few hours and (if I remember) 2-3 GB of disk space. Docker's great because you can run containers that are essentially standalone servers, ie. ESPHome. But this can be a pretty steep learning curve if you're not familiar with Linux.

There's also https://esphome.io/guides/installing_esphome.html - that's a command line ESPHome which will work but quite frankly, not user-friendly at all... at least the above option has a GUI.

If you're not interested in diving that deep, then I'll just build you a firmware. You'll need an ESP32 flashing program and you plug it in to your computer with a USB-C cable (the one it comes with is super-nice), hold the Download button and press the Reset button, then select the ESPHome BIN file to flash... wait... and done... the Web UI can be accessed via something like http://myclock.local or something like that.

Although, you may not want any of this when you get the clock. I'll admit the firmware that comes with it is pretty nice looking... I will never be able to do the sliding seconds it does.

The flash tool, btw is here: https://web.esphome.io/ - yup, an online tool (might only work in Chrome). If you prefer, there's also https://github.com/esphome/esphome-flasher but it even notes on that page they prefer you to use the online tool.

trip5 commented 1 year ago

PS. I may have gone overboard about Docker, etc. I have doubts you're that interested. If anything, take a look at the Readme (the front page here) and the YAML and decide what kind of options you'd like (esp. your clock name ^^) and let me know... and which font(s)... I'll still recommend my own fonts (I'd recommend the Chunky8X or Chunky8 font) but if you know of others, I'm open to (some) experimentation.

drgkt commented 1 year ago

You are right, I don't even know what the clock shows on stock FW!

Here is what I want: 1.Hours : minutes : seconds (blinking colon once a second) 2.Day / date (Mon Jan 01) I guess 1&2 will be alternating. 3.Automatic std / dls switch.

What I would like to control, perhaps with http commands, Tasmota console, other?: A.How often to sync. B.How long to show 1&2 above. C.Disable 3, in case my country decides to stop doing it. Right now it is GMT+2 and at the end of March it will be GMT+3.

Flashing a bin will be fine. I won't be picky about the font. Again, only windows, no Linux.

Based on above, I guess you understood what I need besides Windows I already have and the unit I am expecting.

trip5 commented 1 year ago
  1. I haven't added blinking colon code... and I don't think I can easily. The time isn't actually processed as numbers and replacing a text string every second isn't as easy as it sounds. There's already a selector builtin to switch between 24 and 12-hour time and 24-hour time will show seconds... good enough?
  2. A string like "Mon Jan 01" is kind of long. It will scroll (automatic as part of the display driver). Is that acceptable? I personally use "Jan 01" which is short enough it doesn't scroll... Another clock I use "Mon 01" which is nice too.
  3. See below...

As for the rest... ESPHome isn't Tasmota. ESPHome has a Device WebUI. You can see a preview on the Readme. A. It's part of the YAML and I don't think I can make it configurable later. I like mine at 8 hours (and at power-on of course). Just let me know an ideal hourly rate and I can stick it in the YAML before compile. B. Day/date intervals are configurable in the WebUI (up to 60 seconds each). And it can be turned off, too. C. See below...

As for Daylight time... I'm pretty sure that daylight time is built into the time driver so no adjustment will be needed once the device is flashed... I haven't had reason to test it because my home country (Korea) doesn't use it. It depends on whether or not your country's time specs are already in the time library or not. What's your country? I'm guessing you mean there's some law in the middle of being passed about time changes and it hasn't actually gone into effect yet... It may just be a "do the best you can for now and fix it later" kind of thing.

drgkt commented 1 year ago
  1. Fine, no problem. There is a steady colon?

  2. How many characters is max not to trigger scrolling? Do periods, comas, colons etc count as one character? Stock FW shows day?

  3. The country is Greece. You are right about the law not going into effect yet. If there is no way to disable auto switch via a command then "do the best you can for now and fix it later" as you said. We changed time at the end of last month. The XY did not change by itself.

A. Have you checked how much time it loses or gains in 8 hours? The XY syncs every hour. Is there a reason not to sync every hour? It may miss a sync!

trip5 commented 1 year ago
  1. Steady colon, yes... I've seen some folks switch it a middle-dot or period, but whatever it is, it's steady.
  2. Number of characters depends on character width. It's 32 pixels wide and most characters need 4 but colon or period only takes up 2 but bigger ones like M or W need 6... so roughly 7 or 8 characters total... and there's no shortening the day of the week or months to 2 characters (which would be great).
  3. I'm assuming with the XY you didn't flash it with Tasmota or ESPHome... as I recall, the original firmware on the XY used a simple GMT + or - doesn't it? That wouldn't account for Daylight time. ESPHome uses named timezones in Linux TZ format. You can see them in Wikipedia under the TZ column: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones - there are two types but "Canonical" ones are pretty much guaranteed to work. I've set my ESPHome clocks to my hometown in Canada as a test and they have no problem picking up the correct time with or without Daylight time. I'm assuming your Time would be "Europe/Athens" then...

I am curious to see how long the clock would take to react to Daylight time but I'd be willing to bet it's instantaneous, even if it hasn't synced lately...

Speaking of which, no I have no idea how accurate the clocks are. I imagine they're all as accurate as cheap wristwatches... ie, off by a few seconds a day if you let them... That's why I do every 8 hours... most configs I've seen only sync once in 24 hours which is good enough for most people. I think the ESP probably does a better job than the RTC chip.

But you got me curious so I searched: https://www.instructables.com/TESTED-Timekeeping-on-ESP8266-Arduino-Uno-WITHOUT-/ Spoiler: An ESP is actually pretty good at keeping the time while it's plugged in.

drgkt commented 1 year ago
  1. That's fine.

  2. What do you mean there is no shortening... You cannot use Mo for Monday? MoMay01 I guess is 32 pixels?

  3. I guess canonical Greece/Athens will do. Yes, XY is running stock FW, no auto std/dls switch. If I want this I must flash it Tasmota or ESPHome, correct?

A. As accurate as timekeeping may be, is there a problem with syncing every hour? If you are curious enough (I am),do what Ruben Marc Speybrouck did at 7:59 to see how much yours is off.

B. I have gotten used to the XY time / date display switch times which are 3 seconds each, enough time to get a glimpse of either one, without waiting too long to see the other. But I may play with the GUI on this one.

Software: Please post the links for windows versions of: a.Flashing b.GUI (Unless one does both)

trip5 commented 1 year ago
  1. There's no choice but to use 3 letter abbreviations for Day or Month names ie. Jan, Feb or Mon, Tue.
  2. I already did. You have 2 choices. https://github.com/trip5/EspHome-Led-PixelClock/issues/1#issuecomment-1792912512 and they're both basically a flasher integrated into a GUI. Easy-peasy.

A. No problem. I can put that in your config, set to sync with ntp.pool.org which should automatically locate your nearest time server.

drgkt commented 1 year ago
  1. Then the only choice is Monmm.dd where mm=01-12 and dd=01-31 (Mon01.01)

For the software needed, if I understand correctly, it is ONLY the pictured one? (Screenshot obtained through my phone).

https://www.mediafire.com/view/7jvvm2nqfayvv2z/Screenshot_20231105-235724_Chrome.jpg/file

And how does http://myclock.local work?

trip5 commented 1 year ago

Mon01.01 it is then...

Name your clock "MyClock" and it should appear on your local network as "myclock.local" - so, yeah choose a name that's easy to remember...

Two Choices, 1st is online (PC Only... Chrome but Edge might work) and 2nd is a tool you can download:

The flash tool, btw is here: https://web.esphome.io/ - yup, an online tool (might only work in Chrome). If you prefer, there's also https://github.com/esphome/esphome-flasher but it even notes on that page they prefer you to use the online tool.

drgkt commented 1 year ago

Let the name be "MatrixClock"

"they prefer you to use the online tool" Nothing to install then. And after flashing, if I understood correctly, the only thing I can change is the time / day-date display times and I am using this same tool, correct?

What will matrixclock.local show me or allow me to do?

Can I see the clock's mac address somewhere so I can assign a static IP? Will I see something if I go to that IP with a browser?

Finally, with stock FW the clock does not show Day and does not auto change std/dls time, correct?

trip5 commented 1 year ago

Once the firmware is flashed, only the options in the WebUI (of the clock) are configurable.

BTW A lot of your questions can be answered by looking at my screenshots of the WebUI and the rest of the Readme... You can change the date/time display options with the WebUI. I'm not entirely certain that ESPHome can access the MAC address... but one of the button options will show you the clock's IP address, which can be just as good, since if matrixclock.local doesn't work, you can access the WebUI with the IP address.

If you need it to have a static IP, that is best handled by the router... which should show you the clock's MAC address anyways.

I'm honestly not certain about the stock firmware much. I only tried it out for about 10 minutes before beginning this project. I remember it DOES show the day, month, date, year in a scrolling style. It's honestly pretty nice... but I'm not sure about time zone options... and I don't remember how it syncs time. It looks good but it's not very configurable (as much as I wanted). You'll have to wait and see it with your own eyes.

Download Button functions in your clock will be this: 1 click: toggle date display mode 2 click: toggle 12/24 hour mode long press: display IP address

Usually the Wifi name and password are also hard-coded into the ESPHome configuration. If it can't connect to the Wifi, it makes a hotspot so that you can configure a new Wifi network. Are you comfortable sharing your Wifi name and password here? If not, I don't think it's a problem... it would just save some initial configuration is all.

trip5 commented 1 year ago

Actually, on second thought, no don't share that. It should be fine.

But if you haven't seen this advice before, you should know that if you can, it's good to have different names for the different frequencies of your Wifi access points. Smart devices (as far as I know) only use the 2.4Ghz band even though 5Ghz has become pretty common (your phone and notebook probably use this) and they're now selling routers with 6Ghz...

It is absolutely possible that your 2.4 and 5 Ghz bands both have the same name and your phone and notebook will deal with it just fine by connecting to the fastest one (5 obviously) but I read somewhere it's still best practice to have them use different names... I can't even remember why that is but when I separated mine, my devices stopped have connectivity issues.

trip5 commented 1 year ago

BTW, I'm also adding certain options to do some power-saving... ie, it won't fallback to the hotspot if Wifi is lost, so you may have to reboot the device if the Wifi comes back... I'll test all this out for you when I have time... since it seems like you've settled on your options. Probably the easiest way for me to do that is by flashing my own clock with your firmware and checking everything works as expected.. but give me a few days and I'll have a working firmware for you.

drgkt commented 1 year ago

Thank you for the crash course. Now let's wait for the unit to arrive!

Next I might seek to flash the XY just to get the auto std/dls time switch (keeping everything else the same if possible) or just wait to see if they release new FW...

THANKS AGAIN for the time and effort you put into this!

drgkt commented 1 year ago

Hi there, (share your first name, if you want). Can you make a clock apk for android 2.3.6? I have an old Samsung gt-s5300 laying around, it can no longer sign into Google or playstore but it's size would make a nice clock!

George

trip5 commented 1 year ago

Lol. That's a big ask even if I do know how to program for Android (I don't). You're better off search google for an old apk of a clock app... and just seeing which you one you like... Does it not have a clock built into it?

BTW I've been playing with options... Looks like you can't have the dot... it's too weird, see here: https://youtu.be/5WkJ8hr8UjY

So I removed it and it looks like it should be fine... maybe. https://youtu.be/gPlvR4lQD_0 I'll let it run a few days... I still think it might be a few pixels too long and Wed will be the real test... but err, maybe until March... M is a wider character than others... or we can add some "extra characters (can't just be spaces BTW) maybe like " . . . " so the date looks less insane when it scrolls... lol.

But bad news about the battery... it can eat 3300 mAh a day because it's continuously searching for Wifi. That's terrible. ESPHome has an option to turn off Wifi periodically... so... I'll see how I can implement it... perhaps searching for Wifi when it expects to sync the time seems ideal... in which case, I would definitely say that it would be better to sync less often than every hour...

But again, let me see how it goes... I'll need a few days. If you get your clock, I'll give you a firmware to work with just to see it in the meantime...

trip5 commented 1 year ago

I saw an update to the ESPHome code and that got me coding more than a few hours on these clocks.

I've got it down to about 2000mAh in a day by turning off the Wifi if it can't find its hotspot in 2 minutes (configurable 1-5 mins actually). I'm currently on an ESPHome dev branch because that code won't appear into ESPHome properly until December so I'll give you a bin based on the dev branch after you reply here with my last question... And later, I'll update you to the proper ESPHome version 2023.12.1 or 12.2 when that code is available (the dev branch should be stable but... you never know).

A) I really recommend a shorter date scheme. I did a quick test of a longer date ("MonMay15") and just can't fit. It scrolls like the 1st example above. As I said, I recommend Month + Date ("May 15") or Day + Date ("Mon 15")...

B) If you're really set on having all 3 elements, let's put in a scroll with some spacers... like " . Mon May 15 . " so it doesn't smash into itself. You'll also have to set the date-display to 5+ seconds to get it to display fully once.

So... A or B?

drgkt commented 1 year ago
  1. About the Samsung gt-s5300. I looked but could not find yet an app compatible with android 2.3.6. Play store and others do not have a filter about the android version. Yes it has a clock like all phones, but the key word here is "full screen". The clock widgets it has only take part of the screen, plus an additional command is needed to keep the screen alive.

  2. If doable and easier, let's have 3 screens: a. 23:59:21 b. Mon c. May 31

About the wifi seeking, it seems to me it should seek at boot and every hour (or how often we set the sync rate) and quit seeking 1 minute after if not found.

Just a thought, looking at the capital N I saw in the video...You may consider to use only the 6 lower pixels for capital letters, if it can be done.

https://www.mediafire.com/view/5odwka27rdu9w1g/Screenshot_20231113_073008_Ted.jpg/file

Edit: By the way, does the bigger size fit more stuff or it is just larger to be seen from longer distance?

trip5 commented 1 year ago
  1. It's a puzzler, for sure. You might be better finding a custom rom for the phone that brings it up to at least Android 4... I googled "Samsung gt-s5300 custom rom" and found a few results. This will require patience and probably you'll have to root your phone in the process. There are plenty of tools and how-tos out there. Google is your friend.

  2. It's done. I added a 2nd date screen which splits the time with the first so you can have "Mon" followed by "May 31" and you can specify the total time they're displayed. Choose an even number of seconds because the screen only updates every second. Odd numbers will get divided as 3 = 1 + 2...

About the wifi, no. Turning on the wi-fi is expensive for power so it's better to just have that option permanently turn off the wifi until the next reboot. Anyone using this option is likely to be moving their clock between power-sources anyway and unplugging and plugging it back in it will cause a reboot. The option can be toggled with a special button press.

Regarding fonts, you can look at previews of my fonts here: https://github.com/trip5/Matrix-Fonts - 6 pixels is not narrower. The 8 fonts are stretched out taller. As for that picture you linked to, I'm not sure why the N is actually wider than the M. That goes against standard font rules.

drgkt commented 1 year ago
  1. I found 2. a. https://www.mediafire.com/view/l29g622sf6azgdr/20231114_090548.jpg/file

b. https://www.mediafire.com/view/49fvvb0t8pk46es/20231114_091039.jpg/file

Now if I could find someone to modify the one without seconds it would be great.

  1. Ok. Great!

  2. Wifi: Do you mean sync will occur once on boot only? Why do you care so much about power consumption? It is not a major appliance.

  3. I thought you said that M and W take 6 pixels while most other take 4. In the picture I posted regarding N (6 pixels high) in order to go from upper left line to right lower, 6 pixels are needed (8 if 8 pixels high) and you are right, M takes 5. But I am no expert by far, so I will leave the fonts to you.

  4. You did not comment about the contents of my edit in my last post.

trip5 commented 1 year ago

I did. The 8-series fonts are taller, not wider than the 6-series. The M & W are 5 pixels plus a space. The N is 4 plus a space. Most other characters are 3.

Lol. Your entire commentary on the other thread was about power consumption. And quite frankly, by leaving the wi-fi off, I got it ridiculously low... I'm still running tests but it could run on a 10000mAh power bank for about a week (if the power bank isn't lying about its capacity).

Also, coding timers with actions like that are not so easy. I stand by my idea that you can move it to a battery pack when you lose power and move it back to a wall plug when the blackout ends... and it'll get back on your network and once it's connected, it will stay connected. As long as it's connected to wifi, it will continue checking the time sync.

drgkt commented 1 year ago

In edit in my post before the last, I meant the difference between the 144x40mm unit and the one I ordered 96x26mm. Does the larger one fit more characters?

As for the rest, you know best.

By the way, can the original firmware be saved and flashed back at will?

trip5 commented 1 year ago

I don't know about the clock size. I imagine so. It looks like the same clock but with smaller stuff.

The original firmware... hm. I've got mine uploaded here as a ZIP file.

You'll need to install Python for WIndows and then install esptool through the command line, probably pip install esptool should do that...

Check your device manager to see which COM port the clock shows up as and change COM13 below... if it's showing up as an "unknown device" Windows can automatically install the driver (you'll need to do this anyway to flash it).

Then one of these commands will dump from the ESP chip a 4MB file (I think it's the first):

esptool.py -b 115200 --port COM13 read_flash 0x000000 0x400000 clock_4M.bin
esptool.py -b 115200 --port COM13 read_flash 0x000000 0x100000 clock_1M.bin
esptool.py -b 115200 --port COM13 read_flash 0x000000 0x080000 clock_512K.bin

A bit complex, I realize but just in case your firmware differs from mine, it's the safest. I doubt its different but better safe than sorry.

And yeah you can re-flash the original firmware with the tools I list above.

drgkt commented 1 year ago

https://www.mediafire.com/view/53zow970zv1autc/iMarkup_20231115_014811.jpg/file I guess this is what I need?

And to flash it back what is the command?

trip5 commented 1 year ago

My notes have this: esptool.py -p com13 write_flash 0x0 clock.bin

From the saving part, whichever file has 4 megabytes is the correct one (it's probably the 4M file).

But again, you can probably use one of GUIs I list above just fine. Flashing is easy. Saving flash is hard.

trip5 commented 1 year ago

matrixclock.zip

Here you go. Check the readme for the full preview of what's what. Your timezone has been set using POSIX to Athens which should be fine for Daylight time... unless that time change goes through you mentioned. In which case, it'll be easy enough to change later.

Edit: different BIN because I just realized I hadn't set the hotspot password... it will be the same as the hotspot name.

BTW using a phone to connect it to it, wait a second, your phone should ask you to login. If it doesn't, go to Chrome (or whatever browser is on your phone/notebook) and go to http://192.168.4.1 and then enter your wifi information and it will connect to your wifi network.

You should be able to connect to the clock on your local network with either its IP or http://matrixclock.local.

trip5 commented 1 year ago

Oh and the power consumption experiment is over. The XY-Clock can get ridiculous power-saving by turning off the wi-fi (70% saved) but this clock can also save 50% by turning it off. That's really good.

drgkt commented 1 year ago

Hi. Got bin but no clock yet! LOL I will let you know when it gets here. You may want to upload a non DLS bin... Sincere thanks, George

trip5 commented 1 year ago

I put the POSIX format right in there: time_zone: "EET-2EEST,M3.5.0/3,M10.5.0/4" - which is more than just a timezone... the rules are in there too... I'm not sure how to read it 100% but looks like M3 = 3rd month/March, 5th Sunday (last Sunday?) 3am Daylight activates and deactivates 10th month (October) 5th Sunday at 4am... changing from EET timezone to 2EEST timezone... make sense? Does that sound like your country's current rule?

I got a list of POSIX timezones here: https://gist.github.com/alwynallan/24d96091655391107939 - unfortunately it hasn't been updated in quite some time... it turns out the easiest way to get a locale's POSIX time rule is to extract it from a Linux installation...

More here about POISX and how I decoded the rule: https://developer.ibm.com/articles/au-aix-posix/

I could build another with the new rule quite easily if the dates of the time switch aren't changing... we just change 2EEST to 3EEST I guess? I don't think Linux will catch this rule until your government makes it official... I couldn't find any 3EEST in that github gist above.

trip5 commented 1 year ago

And and worst case, it might be easier to build one with just EET and... there's a time offset option available so you can manually shift the time ahead or back.

drgkt commented 1 year ago

FYI, The last Sunday in October at 04:00 the time is adjusted to 03:00 and... The last Sunday in March at 03:00 the time is adjusted to 04:00.

trip5 commented 1 year ago

So that rule looks right, even if I messed up the interpretation a bit... Let me know if that rule needs updating. I can't see anything through Google about Greece changing its Daylight time yet.

drgkt commented 1 year ago

"Let me know if that rule needs updating" In March, I guess!

We do not know if and when that will go through and you might not be available then to change it.

That is why I suggest to upload a non Daylight savings bin.

As you wrote it, how often it syncs?

Thanks George

trip5 commented 1 year ago

Sync interval is configurable through the UI. Up to you. Between 1 and 24 hours is possible.

Here you go then... EET with no Daylight Savings Time configured: matrixclock-noDST.zip

trip5 commented 11 months ago

Surely you got it by now. How's it going?

BTW, your YAML:

substitutions:
  name: matrixclock
  friendly_name: MatrixClock
  comment: "EHLPClock from Trip5"
  project_version: "2023.11.16"
  project_name: Trip5.EspHomeLedPixelClock
  waiting: "EHLPClock . . . " # shows when waiting for time
  # Time zone can be Olsen type ("Asia/Seoul") # Check https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
  # Or it can be POSIX format: ("PST8PDT,M3.2.0/2:00:00,M11.1.0/2:00:00") - most can be viewed here: https://gist.github.com/alwynallan/24d96091655391107939
  # Read more about how to customize a POSIX format here: https://developer.ibm.com/articles/au-aix-posix/
  time_zone: "EET" # or "EET-2EEST,M3.5.0/3,M10.5.0/4" for 1 hour DST
  # At some point, I figured out how to use alternate time zones... it's a bit weird but useful (and sticks on reboot) - I've commented out the relevant lines for now (check globals & switch)
  # If using Alternate Time Zones, you MUST use POSIX format for both time Zones not Olsen... you will also need the next two substitution lines:
  # time_zone_alt: "AST4ADT,M3.2.0,M11.1.0"
  # time_zone_alt_name: "AST"
  scl_pin: GPIO4
  sda_pin: GPIO5
  clk_pin: GPIO14
  mosi_pin: GPIO13
  cs_pin: GPIO15
  status_led_pin: GPIO2
  download_button_pin: GPIO0
  rotation_pin: GPIO16
  # time and date formats: https://esphome.io/components/time/?highlight=strftime#strftime and https://strftime.org/
  # time_format: "%H:%M" (13:25) / "%H:%M:%S" (13:25:59) / "%I:%M%p" (01:25PM) / "%l:%M%p" (1:25PM)
  time_format12: "%l:%M%p"
  time_format24: "%H:%M:%S"
  # date_format: "%m.%d" (02.28) / "%y.%m.%d" (23.02.28) / "%b %d" (Feb 08) / "%a %d" (Tue 08)
  date_format: "%a" # Mon
  # date_formatB, if used, will split the time of the date display with the above format
  date_formatB: "%b %d" # Jan 01
  date_formatB_on: "true" # false if unused

esphome:
  name: $name
  comment: "$comment"
  project:
    name: "$project_name"
    version: "$project_version"
  on_boot:
    then:
      - ds1307.read_time:
      - globals.set:
          id: date_formatB_on
          value: !lambda 'return "$date_formatB_on";'

esp8266:
  board: esp12e
  restore_from_flash: True

wifi:
  ssid: $name
  password: $name
  reboot_timeout: 0s # reboot if no wifi (must be disabled for wifi to be turned off)
  enable_on_boot: True
  id: wifi_id
  ap:
    ssid: $name
    password: $name
    ap_timeout: 10s # this has to be short, especially if using Stop Seek Wifi
  on_connect:
    - logger.log: "Wifi connected"
    - light.turn_off: led1
    - delay: 1s
    - button.press: display_ip
    - lambda: |-
        id(wifi_stop_seek_time_count) = 0;
        id(my_time)->update();
  on_disconnect:
    - if:
        condition:
          lambda: |-
            return (id(wifi_stop_seek) ==  1);
        then:
          - logger.log: "Wifi disconnected - will be disabled soon (stop seek is on)..."
        else:
          - logger.log: "Wifi disconnected"
    - delay: 1s
    - button.press: display_ip

font:
# Get the fonts from https://github.com/trip5/MatrixClockFonts (but you can use others too!)
  - file: fonts/MatrixChunky8X.bdf
    id: default_font
    glyphs:  |
      ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz°
  - file: fonts/MatrixLight8X.bdf
    id: message_font
    glyphs:  |
      ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz°

time:
  - platform: sntp
    id: my_time
# If you would rather use Home Assistant as a time source, delete the above line and uncomment the next line (but sntp means you can take your clock anywhere)
#  - platform: homeassistant
    timezone: "$time_zone"
    update_interval: 24h # This seems like the longest safe amount of time - actual configured update interval happens under Display
    on_time_sync:
      then:
        - ds1307.write_time:
  - platform: ds1307 # even though it's a ds3231
    id: my_time_ds
    update_interval: never
    timezone: "$time_zone"

api:
  reboot_timeout: 0s # Must be disabled to allow the Wifi enable/disable to function
  services:
# This next section creates the Home Assistant services to allow messages to be displayed
    - service: message
      variables:
        message: string
        message_alive_time: int
        message_display_time: int
        message_clock_time: int
      then:
        - globals.set:
            id: message
            value: !lambda 'return message;'
        - globals.set:
            id: message_alive_time
            value: !lambda 'return message_alive_time;'
        - globals.set:
            id: message_display_time
            value: !lambda 'return message_display_time;'
        - globals.set:
            id: message_clock_time
            value: !lambda 'return message_clock_time;'
        - globals.set:
            id: message_clock_time_count
            value: !lambda 'return message_clock_time;'
        - globals.set:
            id: message_alive_time_count
            value: '0'
        - logger.log:
            format: "Message: %s"
            args: [ 'message.c_str()' ]
        - logger.log:
            format: "Alive Time: %i / Display Time: %i / Clock Time: %i"
            args: [ 'message_alive_time', 'message_display_time' , 'message_clock_time' ]

mdns:
  disabled: false

logger:

ota:
  password: !secret ota_password

web_server:
  port: 80
  ota: true
  include_internal: true

captive_portal:

text_sensor:
  - platform: version
    name: "$friendly_name ESPHome Version"
    hide_timestamp: true
    entity_category: diagnostic
  - platform: wifi_info
    ip_address:
      id: wifi_ip
      name: "$friendly_name IP Address"
      entity_category: diagnostic
    ssid:
      id: wifi_ssid
      name: "$friendly_name SSID"
      entity_category: diagnostic
  - platform: template
    name: "$friendly_name $project_name Version"
    lambda: |-
      return {"$project_version"};
    entity_category: diagnostic
    update_interval: never

output:
  - platform: esp8266_pwm
    id: statusled
    pin:
      number: $status_led_pin
      inverted: true

light:
  - platform: monochromatic
    id: led1
    name: "LED"
    output: statusled
    internal: true

preferences:
  flash_write_interval: 15s # 0 does immediate write to memory with no mem buffer (not recommended) (only saves when persistent variables have changed)

i2c:
  sda: $sda_pin
  scl: $scl_pin
  scan: true
  id: i2cbus

spi:
  clk_pin: $clk_pin
  mosi_pin: $mosi_pin

# Some info how to handle multi_click options: https://community.home-assistant.io/t/esphome-button-held-double-press/136083/10
binary_sensor:
  - platform: status
    name: "$friendly_name Status"
  - platform: gpio
    pin: 
      number: $download_button_pin
      inverted: true
    name: "$friendly_name Download Button"
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    on_multi_click:
      #Click will toggle Date display mode
      - timing:
          - ON for at most 0.5s
          - OFF for at least 0.5s
        then:
          - switch.toggle: date_mode_select
      #Double-Click will toggle 12-hour mode
      - timing:
          - ON for at most 1s
          - OFF for at most 0.5s
          - ON for at most 1s
          - OFF for at least 0.1s
        then:
          - switch.toggle: hour12_select
      #Click once, then Hold will toggle TZ Offset
      - timing:
          - ON for at most 1s
          - OFF for at most 0.5s
          - ON for at least 1s
        then:
          - switch.toggle: tzoffset_on_select
      #Hold 1s will show the IP (or other wifi status)
      - timing:
          - ON for at least 1s
        then:
          - button.press: display_ip
      #Hold 5s will toggle the wifi timeout
      - timing:
          - ON for at least 5s
        then:
          - switch.toggle: wifi_stop_seek_select
  - platform: gpio
    pin: 
      number: $rotation_pin
      inverted: true
    name: "$friendly_name Rotation Mode"
    id: rotation_mode

display:
# https://community.home-assistant.io/t/reusable-function-for-lambdas/296435/10
  - platform: max7219digit
    id: matrix
    cs_pin: $cs_pin
    num_chips: 4
    update_interval: 1s
    reverse_enable: true # true for button-side up / false for down (be warned that if you change these 2 settings you may get a garbled display)
    rotate_chip: 0 # 0 for button-side up (default) / 180 for down (you could switch them but then I would recommend go change the inverted settting for the gpio)
    scroll_mode: continuous
    lambda: |-
      // Display the message if all conditions are met
      if (id(my_time).now().is_valid()) {
        if ((id(message_alive_time) != 0) && (id(message_clock_time_count) >= id(message_clock_time))) {
          id(message_display_time_count) += 1;
          id(message_alive_time_count) += 1;
          auto message_text = id(message);
          it.print(0, 0, id(message_font), message_text.c_str());
          if (id(message_display_time_count) >= id(message_display_time)) {
            id(message_display_time_count) = 0;
            id(message_clock_time_count) = 0;
            if (id(message_alive_time_count) >= id(message_alive_time)) {
              id(message_alive_time_count) = 0;
              id(message_alive_time) = 0;
              ESP_LOGD("main", "Message Alive Time finished.");
            }
          }
        } else {
          if ((id(message_alive_time) != 0) && (id(message_clock_time) != 0)) {
            id(message_clock_time_count) += 1;
            id(message_alive_time_count) += 1;
          }
          // apply offset if needed https://community.home-assistant.io/t/multiple-time-zones/435315/12
          int offset = 0;
          if (id(tzoffset_on) != 0) {
            offset = (int)(60 * 60 * (id(tzoffset)));
          }
          std::time_t tz1time = (id(my_time).now().timestamp + offset);
          std::tm *tz1time_astm = localtime(&tz1time);

          // check if the date should be displayed
          if ((id(date_display_time_count) >= (id(date_display_interval))) && (id(date_display_mode) != 0)) {
            id(date_display_count) += 1;
            // if date_formatB is true, display it for 50% of the time first, after the 1st...
            if ((id(date_formatB_on) == 1) && (id(date_display_count) > (id(date_display_time) / 2))) {
              char displaydtB[20];
              strftime(displaydtB, 20, "$date_formatB", tz1time_astm);
              it.printf((32 / 2), (8 / 2), id(default_font), TextAlign::CENTER, "%s", displaydtB);
            } else {
              char displaydtA[20];
              strftime(displaydtA, 20, "$date_format", tz1time_astm);
              it.printf((32 / 2), (8 / 2), id(default_font), TextAlign::CENTER, "%s", displaydtA);
            }
            if (id(date_display_count) >= id(date_display_time)) {
              id(date_display_time_count) = 0;
            }
          } else {
            id(date_display_count) = 0;
            if (id(date_display_mode) != 0) {
              id(date_display_time_count) += 1;
            }
            // display time
            if (id(hour12_mode) == 1) {
              char displaytm12[20];
              strftime(displaytm12, 20, "$time_format12", tz1time_astm);
              it.printf((32 / 2), (8 / 2), id(default_font), TextAlign::CENTER, "%s", displaytm12);
            } else {
              char displaytm24[20];
              strftime(displaytm24, 20, "$time_format24", tz1time_astm);
              it.printf((32 / 2), (8 / 2), id(default_font), TextAlign::CENTER, "%s", displaytm24);
            }
          }
        }
      } else {
        it.print((32 / 2), (8 / 2), id(default_font), TextAlign::CENTER, "$waiting");
      }
      it.intensity(id(brightness_level));
      // This next section allows the rotation sensor to work - need to directly change settings in the component: https://esphome.io/api/max7219_8h
      if (id(rotation_mode).state == 1) {
        id(matrix)->set_reverse(0);
        id(matrix)->set_chip_orientation(2);
      } else {
        id(matrix)->set_reverse(1);
        id(matrix)->set_chip_orientation(0);
      }

interval:
  - interval: 1min # check if the time should be synced, and if so, do it
    then:
      lambda: |-
        id(sync_time_count) += 1;
        if (id(sync_time_count) >= (id(sync_time) * 60)) {
          id(sync_time_count) = 0;
          if (id(wifi_id).is_connected()) {
            id(my_time)->update();
          }
        }
  - interval: 1s # check if the wifi should be turned off, and if so, do it (or turn on if stop seek has been disabled) and blink led while seeking wifi
    then:
      - lambda: |-
          if (id(wifi_stop_seek) == 1 && !id(wifi_id).is_connected() && !id(wifi_id).is_disabled()){
            id(wifi_stop_seek_time_count) += 1;
            if (id(wifi_stop_seek_time_count) >= id(wifi_stop_seek_time)) {
              id(wifi_stop_seek_time_count) = 0;
              id(led1).turn_off();
              id(wifi_id)->disable();
              id(display_ip).press();
            }
          } else {
            if (id(wifi_stop_seek) == 0 && id(wifi_id).is_disabled()) {
              id(wifi_stop_seek_time_count) = 0;
              id(wifi_id)->enable();
              id(display_ip).press();
            }
          }
          static int blinkled = 0;
          if (!id(wifi_id).is_connected() && !id(wifi_id).is_disabled()) {
            if (id(wifi_stop_seek) == 0) {
              auto call = id(led1).toggle();
              call.perform();
              //id(led1).toggle();
            } else {
              if (++blinkled & 1) {
                auto call = id(led1).toggle();
                call.perform();
              }
            }
          }

globals:
   - id: brightness_level
     type: int
     restore_value: true
     initial_value: '1'
   - id: hour12_mode
     type: bool
     restore_value: true
     initial_value: 'false'
   - id: date_formatB_on
     type: bool
     restore_value: false
     initial_value: 'false'
   - id: tzoffset
     type: float
     restore_value: true
     initial_value: '0'
   - id: tzoffset_on
     type: bool
     restore_value: true
     initial_value: 'false'
# Alt Time Zone variable
#   - id: tz_alt
#     type: bool
#     restore_value: true
#     initial_value: 'false'
   - id: message
     type: std::string
     restore_value: false
     initial_value: ''
   - id: message_display_time
     type: int
     restore_value: false
     initial_value: '0'
   - id: message_clock_time
     type: int
     restore_value: false
     initial_value: '0'
   - id: message_alive_time
     type: int
     restore_value: false
     initial_value: '0'
   - id: message_display_time_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: message_clock_time_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: message_alive_time_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: date_display_mode
     type: bool
     restore_value: true
     initial_value: 'false'
   - id: date_display_interval
     type: int
     restore_value: true
     initial_value: '12'
   - id: date_display_time
     type: int
     restore_value: true
     initial_value: '3'
   - id: date_display_time_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: date_display_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: sync_time
     type: int
     restore_value: true
     initial_value: '8'
   - id: sync_time_count
     type: int
     restore_value: false
     initial_value: '0'
   - id: wifi_stop_seek
     type: bool
     restore_value: true
     initial_value: 'false'
   - id: wifi_stop_seek_time
     type: int
     restore_value: true
     initial_value: '180'
   - id: wifi_stop_seek_time_count
     type: int
     restore_value: false
     initial_value: '0'

button:
  - platform: restart
    id: "Restart"
    internal: true
  - platform: template
    id: display_ip
    name: "Display IP Address"
    internal: true
    on_press:
      - if:
          condition:
            not:
              wifi.connected:
          then:
            - lambda: |-
                if (id(wifi_id).is_disabled()) {
                  id(message) = ("Wifi Off");
                } else {
                  id(message) = ("No Wifi");
                }
                id(message_alive_time) = 3;
                id(message_display_time) = 3;
                id(message_clock_time) = 0;
          else:
            - lambda: |-
                id(message) = (" IP : " + id(wifi_ip).state + " . . . . ");
                id(message_alive_time) = 9;
                id(message_display_time) = 9;
                id(message_clock_time) = 0;

switch:
  - platform: template
    name: "$friendly_name 12 Hour Mode"
    id: hour12_select
    restore_mode: RESTORE_DEFAULT_OFF
    lambda: |-
      if (id(hour12_mode)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
        lambda: |-
          id(hour12_mode) = true;
    turn_off_action:
        lambda: |-
          id(hour12_mode) = false;
  - platform: template
    name: "$friendly_name Time Zone Offset On"
    id: tzoffset_on_select
    restore_mode: RESTORE_DEFAULT_OFF
    lambda: |-
      if (id(tzoffset_on)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
        lambda: |-
          id(tzoffset_on) = true;
    turn_off_action:
        lambda: |-
          id(tzoffset_on) = false;
# Alt Time Zone selector / activator
#  - platform: template
#    name: "$friendly_name Alternate Time ($time_zone_alt_name)"
#    id: tz_alt_select
#    restore_mode: RESTORE_DEFAULT_OFF
#    lambda: |-
#      if (id(tz_alt)) {
#        return true;
#      } else {
#        return false;
#      }
#    turn_on_action:
#        lambda: |-
#          id(tz_alt) = true;
#          id(my_time)->set_timezone("$time_zone_alt");
#          id(my_time_ds)->set_timezone("$time_zone_alt");
#          id(my_time)->call_setup();
#    turn_off_action:
#        lambda: |-
#          id(tz_alt) = false;
#          id(my_time)->set_timezone("$time_zone");
#          id(my_time_ds)->set_timezone("$time_zone");
#          id(my_time)->call_setup();
  - platform: template
    name: "$friendly_name Date Display Mode"
    id: date_mode_select
    restore_mode: RESTORE_DEFAULT_OFF
    lambda: |-
      if (id(date_display_mode)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
        - lambda: |-
            id(date_display_mode) = true;
            id(message) = ("Date on");
            id(message_alive_time) = 1;
            id(message_display_time) = 1;
            id(message_clock_time) = 0;
    turn_off_action:
        - lambda: |-
            id(date_display_mode) = false;
            id(message) = ("Date off");
            id(message_alive_time) = 1;
            id(message_display_time) = 1;
            id(message_clock_time) = 0;
  - platform: template
    name: "$friendly_name Wifi Stop Seek"
    id: wifi_stop_seek_select
    restore_mode: RESTORE_DEFAULT_OFF
    lambda: |-
      if (id(wifi_stop_seek)) {
        return true;
      } else {
        return false;
      }
    turn_on_action:
        - delay: 1s
        - lambda: |-
            id(wifi_stop_seek) = true;
            id(message) = ("StopSeek");
            id(message_alive_time) = 1;
            id(message_display_time) = 1;
            id(message_clock_time) = 0;
    turn_off_action:
        - delay: 1s
        - lambda: |-
            id(wifi_stop_seek) = false;
            id(message) = ("Seek...");
            id(message_alive_time) = 1;
            id(message_display_time) = 1;
            id(message_clock_time) = 0;

number:
  - platform: template
    name: "$friendly_name Brightness"
    id: brightness_select
    restore_value: true
    optimistic: true
    step: 1
    min_value: 0
    max_value: 15
    mode: slider
    set_action:
      then:
        lambda: |-
          id(brightness_level) = x;
          ESP_LOGD("main", "Brightness: %d", id(brightness_level));
  - platform: template
    name: "$friendly_name Date Display Interval"
    id: date_display_interval_select
    restore_value: true
    optimistic: true
    step: 1
    min_value: 1
    max_value: 60
    mode: box
    unit_of_measurement: seconds
    set_action:
      then:
        lambda: |-
          id(date_display_interval) = x;
          ESP_LOGD("main", "Date Display Interval: %d", id(date_display_interval));
  - platform: template
    name: "$friendly_name Date Display Time"
    id: date_display_time_select
    restore_value: true
    optimistic: true
    step: 1
    min_value: 1
    max_value: 60
    mode: box
    unit_of_measurement: seconds
    set_action:
      then:
        lambda: |-
          id(date_display_time) = x;
          ESP_LOGD("main", "Date Display Time: %d", id(date_display_time));
  - platform: template
    name: "$friendly_name Time Zone Offset"
    id: tzoffset_select
    restore_value: true
    optimistic: true
    step: 0.25
    min_value: -26
    max_value: 26
    initial_value: 0
    mode: box
    unit_of_measurement: hours
    set_action:
      then:
        lambda: |-
          id(tzoffset) = x;
          ESP_LOGD("main", "Time Zone Offset: %.2f", id(tzoffset));
  - platform: template
    name: "$friendly_name Time Sync Interval (hours)"
    id: sync_time_select
    restore_value: true
    optimistic: true
    step: 1
    min_value: 1
    max_value: 24
    mode: box
    set_action:
      then:
        lambda: |-
          id(sync_time) = x;
          ESP_LOGD("main", "Time Sync Interval (hours): %d", id(sync_time));
  - platform: template
    name: "$friendly_name Wifi Stop Seek Time (seconds)"
    id: wifi_stop_seek_time_select
    restore_value: true
    optimistic: true
    step: 5
    min_value: 60
    max_value: 300
    mode: box
    set_action:
      then:
        lambda: |-
          id(sync_time) = x;
          ESP_LOGD("main", "Wifi Stop Seek Time (seconds): %d", id(wifi_stop_seek_time));
trip5 commented 11 months ago

Actually I really do wonder if it's OK.

I recently discovered that usually by leaving the api: section in, it will try to connect to Home Assistant and reboot if it does not.

I may have over-ridden that behavior with reboot_timeout: 0s though... not sure. Will you let me know?

drgkt commented 11 months ago

Hi there! Unfortunately, I have not received it yet! I will let you know. G

trip5 commented 10 months ago

@drgkt : Is it still in the mail? China to Greece seems to take awhile!

BTW, I've been working on adding bilingualism to these clocks and I've been adding Greek to my font...

The only question I have is... What are the common 3-5 letter abbreviations for Greek Weekdays? I think some of them have too long names to fit on the clock screen (Saturday doesn't fit either). With Google, it was easy enough to get the month abbreviations...

        - "Jan -> Ιαν"
        - "Feb -> Φεβ"
        - "Mar -> Μάρ"
        - "Apr -> Απρ"
        - "May -> Μάι"
        - "Jun -> Ιούν"
        - "Jul -> Ιούλ"
        - "Aug -> Αύγ"
        - "Sep -> Σεπ"
        - "Oct -> Οκτ"
        - "Nov -> Νοέ"
        - "Dec -> Δεκ"

By the way, following the same method as above, the day of the week will be displayed just before the Month and Date of the month. Copy and paste, edit the next bit and get back to me and I'll some bilingual firmware for you (check out the main page for an animation of what it looks like in Korean).

        - "Sun -> ?"
        - "Mon -> ?"
        - "Tue -> ?"
        - "Wed -> ?"
        - "Thu -> ?"
        - "Fri -> ?"
        - "Sat -> ?"
drgkt commented 10 months ago

Hi there. It finally came after New Year and I left it run a couple of days on stock FW. Once every minute at 45th second it shows MON 08 JAN 2024 in scrolling mode which can be turned off but the frequency cannot be changed. There is a flexible DST option that let's you specify several parameters.

Caveats: Obviously the Day / Date frequency, BUT most importantly there is a bug that every 2-3 days the unit loses time and goes to THU 07 FEB 2036! Strangely, the Web interface still works where all you have to do is click on Save & Reboot since all parameters are still there.

Because of this, there is no point to save stock FW, but I want to do it for the experience. I am putting it off because I broke my leg in the meantime! Let me install Python for Windows first.

20240108_214914

trip5 commented 10 months ago

[ deleted ]

Here you go. Updated Firmwares and YAML. I tested it on my own clock real quick. Seems to work without crashing. Greek font that I recently made looks pretty neat. Let me know if any characters look out of place. It often takes a native to see if something looks off. I especially had trouble with γ and ζ and ξ - and I stylized the iota characters to my own taste so I'm not sure how they look to you.

Any issues with Greek fonts should be handled over at https://github.com/trip5/EspHome-Led-PixelClock - you can zoom on the font pics if you have time and let me know if any seem even a little bit wrong. And I opened an issue for you or any other Greeks to let me know.

trip5 commented 10 months ago

Oh and wow. Good luck with the leg.

trip5 commented 10 months ago

OMG. You're not gonna be able to access the hotspot of those firmwares... I left the AP password as my own.

The password in these firmwares match the name of the Hotspot created. (Also some minor fixes applied.)

matrixclock.zip