MagicMirror Forum

    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unsolved
    • Solved
    • MagicMirror² Repository
    • Documentation
    • Donate
    • Discord
    1. Home
    2. j.e.f.f
    J
    • Profile
    • Following 0
    • Followers 21
    • Topics 27
    • Posts 681
    • Best 164
    • Controversial 1
    • Groups 2

    j.e.f.f

    @j.e.f.f

    Project Sponsor Module Developer

    248
    Reputation
    13796
    Profile views
    681
    Posts
    21
    Followers
    0
    Following
    Joined Last Online

    j.e.f.f Unfollow Follow
    Project Sponsor Module Developer

    Best posts made by j.e.f.f

    • My display so far...

      First, many MANY thanks to all of the programmers who make this project possible. I’m waiting on delivery of my screen and glass, meanwhile I’ve been doing my configurations on my laptop. I’ve modified pretty much every module’s CSS and markup to get this.

      Here’s what I have so far. I can share my files with anyone who wants this but know that you’re stuck having to maintain your own versions of each of the modules – an update will overwrite the custom markup, so you’ll want to be careful if you need to update from git source.

      0_1493777449919_Screen-shot-comp-2-may-2017.png

      Can’t wait to get this on the wall!

      posted in Showcase
      J
      j.e.f.f
    • CSS 101 - Getting started with CSS and understanding how CSS works

      I see quite a few threads come up with folks asking how to write CSS in order to customize this and that, and I’ve come across a few modules that have CSS written in such a way that they inadvertently affect the layout of other modules.

      I thought I’d write this thread to shine some light on the following topics:

      • Basics of CSS
      • When one rule takes precedence over another
      • How to use MagicMirror in Dev mode to inspect an element to see the CSS rules applied to a given element
      • How to write good CSS in your modules so that they will not conflict with styles in another module.

      NOTE below where I’ve included HTML markup examples, you’ll notice a space between the opening bracket and the element name. This is only because there is a bug with mark down that hides the details of HTML. For example if I write

      < div>
      

      I’m referring to an HTML div element.

      Basics of CSS

      CSS stands for Cascading Style Sheets. They are called cascading because various style rules will be applied to given element based on a predefined set of “cascading” rules of precedence. More on rules of precedence later.

      CSS works in tandem with HTML markup. In case you didn’t know, MagicMirror uses HTML for its interface. It is essentially a web page.

      She has Class! She has Style!

      CSS styles can be applied to HTML elements in two ways:

      Inline:

      < span style="background-color: red">
      

      or through applying one or more classes to the element:

      < span class="text-color-blue">
      

      The class text-color-blue will correspond to a rule in your style sheet (e.g.: custom.css)

      It is possible to combine class and style in the same element:

      < span style="background-color: red" class="text-color-blue">
      

      Assuming we’ve written a rule in our style sheet called text-color-blue that turns the text color within the element to blue, the result will be a visual element with a red background and blue text.

      In our case, we’ll be dealing almost exclusively with external style sheets, so for the remainder of this tutorial I will focus on the class method of applying CSS.

      CSS rules in your style sheet will look something like this:

        selector {
          property: style;
          property: style;
          property: style;
        }
      

      A rule starts with a selector and contains one or more properties to which you want to apply a style. Properties and their respective styles are separated by the colon : character, and pairs of properties and styles are separated by the semicolon ;. Keep in mind that white space here does not matter. The above can also be written as follows:

      selector{property:style;property:style;property:style;}
      

      I use multiple lines and indentation to make my rules easier to read. You should too, but your style might vary from mine. That’s OK as long as your rules can easily be read by anyone who might need to.

      A selector will specify the element you wish to target with your rule.

      Elements can be targeted by name:

      /*
        This will colour the text of all span elements red
      */
      span {
        color: red;
      }
      

      or by class. Class names are prefixed with a dot in your CSS:

      /*
        This will colour the text red for all elements with the class make-me-red:
      */
      .make-me-red { /* notice the dot */
        color: red;
      }
      

      or by ID. rules targeting IDs are prefixed with the # symbol in your CSS

      /*
        This will colour the text red within the element with the ID my-id:
      */
      #my-id { /* notice the hash symbol */
        color: red;
      }
      

      It’s important to note that the dot for classes and the # symbol for IDs are only used in your CSS rules. The HTML markup doesn’t use these symbols:

      < span id="my-id" class="make-me-red">
      

      Selectors can be combined to make a more specific rule:

      span.make-me-red#my-id {
        color: red;
      }
      

      This will apply ONLY to span elements with the class make-me-red AND the ID my-id.

      Selectors can indicate element hierarchy:

      .outer-class .inner-class {
        color: red;
      }
      

      This means the rule will be be applied to any element with the class .inner-class that exists inside another element with the class outer-class. This rule will NOT apply to an element with the class inner-class that is NOT a child of an element with the class outer-class.

      < div class="outer-class">
        < span class="inner-class">This element gets red text colour< /span>
      < /div>
      
      < div class="inner-class">This element does not< /div>
      

      NOTE This is the only instance where white space matters. The space between the two classes is important. These two selectors mean two very different things:

      .outer-class .inner-class {
        color: red;
      }
      .outer-class.inner-class {
        color: red;
      }
      

      This first applies a red text colour to an element with the class name inner-class that is a child of another element with the class name outer-class. The second applies a red text colour to an element that has BOTH outer-class and inner-class classes.

      It should be noted that while you can target children of elements, you cannot target the parent of an element. That is while I can target any element with the class outer-class, any element with the class inner-class or any element with the class inner-class inside an element with the class outer-class, I cannot target elements with the class outer-class that specifically contain a child element with the class inner-class.

      Child elements inherit rules applied to the parent

      Consider this markup:

      < div class="outer-container">
        Some text here
        < span>More text here< /span>
      < /div>
      

      And this CSS rule:

      .outer-container {
        color: blue,
      }
      

      The way CSS works, if you apply a rule to an element, that rule will persist down through it’s child elements. In this case, not only does the text Some text here appear blue, but so does the text More text here in the child span element. If I wanted the inner span text to be a different colour, I would need to write another rule targeting it specifically. There are some exceptions to this rule. Generally setting sizing or positioning (width, height, top, left, etc.) does not get inherited by child elements. But most content formatting properties (font-family, font-size, color, line-height etc.) do.

      This is the essence of CSS – you write specific rules to target only those elements to which you wish to have styles applied, and those styles will cascade to child elements, unless you override their styling with a more specific rule.

      CSS Properties and Units

      there are MANY CSS properties that you can style, and several different ways you can specify quantity. I won’t get into the entire list but I’ll cover some examples of commonly used ones below. If you want to know every possible property and possible values, start here:
      https://www.w3schools.com/css/

      .region.top.left {
        width: 100%;
        max-width: 400px;
        min-width: 10vw;
      }
      

      In the above example, I’ve applied style rules to elements with the classes region, top, and left. Let’s examine each rule individually here:

      width: 100%;
      

      When you use percentage units, you are specifying a percentage of the width of the element’s parent.

      Now consider the following example:

      height: 100%;
      

      Reasonably you might expect that this means you wish the height of the element to be 100% of the height of its parent. In practise this is seldom true… the percentage will still refer to the width of the parent. So the above rule will size the element’s height to 100% of the width of the parent element. I know… confusing… just remember when you see sizes in percentage, it almost always refers to a percentage of the parent’s width

      Now lets take a look at the next property:

      max-width: 400px;
      

      When you see px this is specifying the value in pixels. This one is the most easy to understand, as it directly relates to the screen’s resolution. If you specify a width of 10px, your element will be 10 pixels wide. But also take a look at the property name:

      max-width
      

      This sets a limit for width of the element. The first property says size the element to 100% of the width of its parent. The second property says limit this the width to 400 pixels. So if they size of the parent element is 700 pixels, the targeted element will be 400 pixels. Similarly the next property sets a minimum width:

      min-width: 10vw;
      

      the metric is vw is a lesser know unit, but it is quite useful. It stands for viewport width. A value of 100vw is equal to the entire width of the browser window. If the element is within an iframe, then 100vw refers to the entire width of the iframe. The above rule states that the element shall be no narrower than 10vh, or 1/10th the width of the viewport. Similarly you can specify units in terms of viewport height:

      height: 50vh;
      

      This sizes the element to be half as tall as the viewport.

      A couple of links that describe CSS units in more detail:
      https://www.w3schools.com/cssref/css_units.asp
      https://www.tutorialspoint.com/css/css_measurement_units.htm

      So why might you use one unit versus another? A great question, and if you can fully understand this, you’ll be well on your way to mastering your designs. One thing that HTML is well suited for is adapting to any screen size. One thing that frustrates novices is HTML’s tendency to adapt to any screen size! Using pixels is easy to understand, because it directly relates to the screen’s resolution. A pixel is a pixel is a pixel (unless it isn’t… this starts to be less true when we get into dealing with high density screens such as an iPhone’s retina screen.), but 5vw, for example, is relative to the viewport’s width. Depending on the screenesize, window size, iframe size, etc, 5vw can be anything. So why would I use it instead of pixels? Well say I needed to fit a specific phrase to always fit on one line, regardless of screen size. I could write a rule such as this:

      .phrase-on-one-line {
        font-size: 2vw;
      }
      

      This now sizes the text relative to the viewport’s width. As long as I have found a vw value for the text where it all fits on one line, it will ALWAYS fit on one line, regardless of the viewpoirt’s width. Of course, this can lead to text being extremely small or extremely large, so use with caution. But if you take a look at the CSS for my MMM-MyWordClock module, you’ll see that I’ve used text sized in terms of vw and vh to adapt to any 16:9 screen regardless of pixel resolution. If you were to view this module on two 32" TVs, one 720p, one 1080p, text size will be exactly the same size on each. The text will just be sharper on the 1080p screen as the screen has a higher pixel density.

      There are many MANY articles that describe the benefits and pitfalls to using units other than pixels in your designs. Spend some time Googling these to get a handle on when to use one over another.

      Order of Precedence

      CSS rules are applied to the target element following a standard set of rules:

      1. A more specific rule will override a less specific rule, or the second rule of equal specificity will override the first.
      2. Inline styles will override styles in your CSS file.
      3. Styles with the !important flag will override anything, regardless of specificity

      Let’s explore each of these with examples:

      Rule #1
      A more specific rule will override a less specific rule, or the second rule of equal specificity will override the first.

      Consider this markup:

      < div class="name-block">
        < span class="first-name">Mike< /span>
        < span class="last-name">Crotch< /span>
        < span class="date-of-birth">1-Jan-1975< /span>
      < /div>
      
      < div class="name-block small">
        < span class="first-name">Oliver< /span>
        < span class="last-name">Closeoff< /span>
        < span class="date-of-birth">13-Apr-1981< /span>
      < /div>
      

      Let’s first write some general rules for text size and color:

      span {
        color: #000000;
        font-size: 16px;
      }
      

      This will set text in all span elements to 16 pixels in size and black colour (Don’t understand that #000000 means black? See this: https://www.w3schools.com/css/css_colors.asp).

      Now let’s write some more specific rules to selectively override the above:

      span.first-name {
        font-size: 24px;
      }
      
      .name-block.small {
        font-size: 12px;
      }
      
      .name-block.small span.first-name {
        font-size: 16px;
      }
      
      span.date-of-birth {
        color: #ff0000;
      }
      

      Putting it all together, our style sheet looks like this (I’ve added comments to number each rule):

      /* 1. */
      span {
        color: #000000;
        font-size: 16px;
      }
      
      /* 2. */
      span.first-name {
        font-size: 24px;
      }
      
      /* 3. */
      .name-block.small {
        font-size: 12px;
      }
      
      /* 4. */
      .name-block.small span.first-name {
        font-size: 16px;
      }
      
      /* 5. */
      span.date-of-birth {
        color: #ff0000;
      }
      

      Here’s how the style sheet instucts the browser to apply styling:

      • Rule # 1 states that all text within span elements should be sized to 16px and coloured black.
      • Rule # 2 states that all span elements with the class first-name shall be 24 pixels in size. This overrides rule #1 because it is more specific
      • Rule # 3 states that all text should be 12 pixels in size. This overrides rule # 1 because it is more specific, and it overrides rule #2 because it is equally specific but specified second.
      • Rule # 4 states that span elements with the class first-name within an element with both the name-block and small classes shall be sized to 16 pixels. This overrides rule #3 because it is more specific.
      • Rule # 5 changes the text color of span elements with the class name date-of-birth to red. This overrides rule #1 because it is more specific.

      As far as I can gather, custom.css is loaded into MagicMirror after the default style sheet. So in order to override default styles you’ll need to write styles that are at least equally as specific as the default styles for them to be applied.

      The above example is a very common use case for CSS. Set a base style that will be used throughout, and then override in particular places as necessary.

      Rule # 2
      Inline styles will override styles in your CSS file.

      Consider this markup:

      < span class="small-text" style="font-size:24px">Happy Text< /span>
      

      and this style sheet:

      span.small-text {
        font-size: 12px;
      }
      

      The inline style takes precedence, and the text will subsequently be sized to 24 pixels. It doesn’t matter how specific the rule is applied in the style sheet. Inline styles always supercede styles applied through the style sheet…

      …unless…

      Rule # 3
      Styles with the !important flag will override anything, regardless of specificity.

      Let’s use the same markup as in the previous example:

      < span class="small-text" style="font-size:24px">Happy Text< /span>
      

      But this time, we’ll add a little tweak to the rule in the style sheet:

      span.small-text {
        font-size: 12px !important;
      }
      

      Now text will be sized to 12px, as the !important flag overrides any font-size rule applied to the element, regardless of whether another rule is more specific or if font-size has been specified inline in the element.

      Alas, this is NOT a situation you want to find yourself in very often. Ideally, you never want to be in a position where you are forced to used !important to apply your style. Here are the common reasons why people will use !important in their style sheets.

      1. They can’t figure out why their style isn’t being applied.

      In this case it is almost always a situation where you are trying to apply a style to which a more specific rule already applies. You need to write your rule to be even more specific. In the next section I’m going to show you how to use the inspector in the browser to figure out what styles are currently being applied so that you can confidently write a rule to override it.

      2. There are inline styles in the markup that can only be overriden with the !important flag.

      This is certainly a valid reason, but one that could have been avoided if the module developer had some foresight as to what the implications of using inline styles are. As a module developer, try to avoid using inline styles unless absolutely necessary. At all costs favour using classes in your style sheet instead. This makes it easier for others to customize the look of your module for their needs.

      3. They are just being lazy.

      To this I say short term gain, but long term pain. This leads down a slippery slope of !important flags everywhere in your style sheet. After all, you can only override !important with another !important. It also breaks the cascading nature of CSS… you ultimately end up writing more CSS instead of letting base styles cascade to your otherwise unstyled elements.

      If you’ve take away nothing else from this section, it’s that you should always try to be in situation one, that is writing specific rules to target specific things. As a module developer, try not to inject inline styles in your markup unless absolutely necessary, so that those wishing to customize your module will have an easier time of it.

      The Magical Inspector

      If you’re really serious about customizing your CSS, then Electron’s inspector is an invaluable tool. Learning how to use it will help you write very targeted CSS rules. It also has the added benefit of allowing you to try out CSS changes and have the results appear real-time. Great if you’re trying to pixel-nudge something into the exact perfect location.

      There a few ways you can make use of the inspector.

      The most straightforward way is to start MagicMirror in development mode. Note that to do this, you need to have a keyboard and mouse connected to your Raspberry Pi. If MagicMirror is currently running, you’ll want to stop it first. Assuming you’ve used PM2 to manage Magic mirror, issue the command pm2 stop mm or pm2 stop MagicMirror depending on how you’ve configured it. If you’re just starting Magic Mirror manually with the npm start command, just hit ctrl-c in the terminal window from where you started it.

      Now in your terminal, make sure you are in the MagicMirror directory. If you’ve used the default installation, type the following:

      cd ~/MagicMirror
      

      Now issue the following command to start MagicMirror in development mode.

      npm start dev
      

      This will start MagicMirror in a split screen layout. One side will be the Magic Mirror that you know and love, the other side will be the developer tools console. There’s a whole lot going on here, but don’t be intimidated! We’re going to focus on just one part of it. By default you will be in the element inspector mode.

      Familiarize yourself with the items right along the top. First you will see an icon that looks like a mouse cursor clicking in a square. Remember this icon, as we’ll come back to this one shortly. The next icon toggles the mobile toolbar. You can ignore this altogether. Finally you will see a bunch of text items: “Elements”, “Console”, “Sources” etc. You’ll only need to concern yourself with “Elements.” If you get lost and accidentally end up switching to another mode, just click on “Elements” and you’ll be right back to where you need to be.

      Below these items, the developer tools are divided up into three sections:

      • HTML Markup
      • CSS Styles
      • Error Console

      Depending on how wide your dev tools window currently is, you might see HTML and CSS side-by-side, with the error console below, or you may see HTML on top, CSS in the middle, and the error console on the bottom.

      We’re going to spend most of our time in the HTML and CSS sections, and if you need more space, each of the sections are adjustable by dragging the divisions between two regions. The entire dev tools window can also be resized left and right if you need more space to work. For now, let’s resize the error console to use as little space as possible.

      Remember that first icon in the top right? Click it! It should turn blue. Click it again to turn it black. When it’s blue, you’ve activated a mode where you can click on items in Magic Mirror’s display to inspect them. Let’s try clicking on the icon now to turn it blue, then click on anything in Magic Mirror. Now look at the CSS section. You will see all of the CSS rules applied to the particular element you’ve clicked on. Also take a look in the HTML panel. The element you’ve clicked on should be highlighted. You can also click on things in the HTML panel to see what CSS is applied. I find it useful to click on elements directly in Magic Mirror to get close to the item I need to inspect, then I use the HTML panel to drill in to the exact element I need to inspect.

      When you’re inspecting an element, it’s common to see many rules applied to it. These will be your clues to figure out how specific a rule you need to write in order to override an existing style with one of your own.

      Let’s take a look at this screenshot:

      0_1521508989305_inspector.jpg

      I’ve clicked on one of the rows in MMM-MyScoreboard. I can see from CSS panel that following rules are being applied:

      .MMM-MyScoreboard .box-score.oneLine, .MMM-MyScoreboard .box-score.oneLineWithLogos {
        height: 30px;
      }
      
      .MMM-MyScoreboard .box-score {
        min-width: 300px;
        height: 70px;
        position: relative;
        text-align: left;
        border-bottom: solid 1px #222;
      }
      

      Now there’s something new going on here that we haven’t seen before. Take a look at the first rule. It has two selectors separated by a comma. This is a technique that allows you to apply the same rule to multiple selectors! It might be easier to see what’s going on when rewritten as follows:

      .MMM-MyScoreboard .box-score.oneLine,
      .MMM-MyScoreboard .box-score.oneLineWithLogos {
        height: 30px;
      }
      

      This means that for elements with either box-score AND oneLine OR elements with box-score AND oneLineWithLogos that are children of an element with the class MMM-MyScoreboard will be sized to 30 pixels in height.

      We’re also seeing one rule overriding another here. The first rule, being more specific, overrides the height value of that specified in the second rule. By default, box-score elements are 70 pixels tall, but when either the oneLine or oneLineWithLogos class is added, the size becomes 30 pixels.

      The inspector makes this easy to see. Notice that the 70 pixel height property in the second rule is shown in strike through text? This really quickly lets you know that the height rule is being overriden by something else. You then know to look for a more specific rule to see where it’s getting its height.

      0_1521509021120_Screen Shot 2018-03-19 at 11.37.02 PM.png

      So if I wanted to override the height, I’d need to write a rule that is more specific than the first rule. There are many different ways you could accomplish this. Here’s one:

      Take a look at the HTML panel. You’ll see that the highlighted element is a div. We could simply add the element name to the rule to make it more specific, like so:

      .MMM-MyScoreboard div.box-score.oneLine,
      .MMM-MyScoreboard div.box-score.oneLineWithLogos {
        height: 50px;
      }
      

      Here I’ve increased the height to 50 pixels, and by adding the element name to the selectors, I’ve made the rule more specific. This means that it will now take precendence. Another way you could do it is to add your own custom class in config.js for the particular module, like this:

      {
        module: "MMM-MyScoreboard",
        position: "top_center",
        classes: "jeff-custom", //custom class added here
        header: "Scoreboard",
        config: {
          ...
        }
      }
      

      You can add any class you want, and it will be added to the outer wrapper element of the module. So you could write a more specific rule like so:

      .MMM-MyScoreboard.jeff-custom .box-score.oneLine,
      .MMM-MyScoreboard.jeff-custom .box-score.oneLineWithLogos {
        height: 50px;
      }
      

      But before you jump in, the inspector has one more trick up its sleeve. You can try out CSS changes right in the browser and see the effect rendered real time! Say we knew we wanted to make the box-score element taller, but we were unsure how MUCH taller to make it, we can click on the property we wanted to change and change it. In the CSS panel, I could click on the 30px value and change it to whatever I wanted to try. Too much? I can dial in a smaller number. Getting close? I can use the arrow keys to nudge the value one pixel at a time. Not quite right? If I click at the bottom of the property list, and can type in a new property and value. Say I wanted to add a border… I’d click at the bottom of the list, and type border. Then I’d hit the tab key and type in the value 1px solid #FFFFFF.

      The best part is none of this is permanenet. If you really foobar the layout, just refresh your browser and you’ll reset to what it was before you started tinkering. Changes do not become permanent until you write them in to your CSS file.

      By the way, did you notice something new back there? In the example above where I’ve set a border, I specified a bunch of properties all in one go: the border width, the border style, and the border colour. This is known as shorthand. There are plenty of CSS properties where you can specify shorthand values.

      Using the above example:

      .MMM-MyScoreboard.jeff-custom .box-score.oneLine {
        border: solid 1px #FFFFFF;
      }
      

      this is the same as writing:

      .MMM-MyScoreboard.jeff-custom .box-score.oneLine {
        border-style: solid;
        border-width: 1px;
        border-color: #FFFFFF;
      }
      

      A Better Way

      I’m sure as you were following along with this you struggled with the lack of screen space, especially if your Magic Mirror install uses a monitor in vertical orientation. This is a tedious way to work…

      A better way is to install Magic Mirror on your desktop PC or laptop, and do all of your CSS customization there, and then transfer it to your Raspberry Pi when you’re all done. Here’s what I find works best for me:

      Follow the instructions here to do a manual install:
      https://github.com/MichMich/MagicMirror#manual-installation

      Don’t bother with setting up PM2 for autostart. We just want to be able to run Magic Mirror occasionally for the purpose of customizing our CSS. Once your install is done, install whatever modules you want to modify and customize as usual.

      Now here’s the key: start Magic Mirror in server only mode. There’s nothing wrong with with happily typing npm start but this will start Electron and take over your display in full-screen mode. Instead, issue this command:

      node serveronly
      

      Now open Google Chrome – yes that’s right: Chrome – and point it to the following URL:

      http://localhost:8080/
      

      You should now see Magic Mirror’s familiar display. Now go to View -> Developer -> Developer Tools. Look familiar? Not quite? OK… Remember the tip I gave you before? If you ever get lost, just click on “Elements” at the top. That should start looking a bit more familiar!

      The Electron browser uses the same underlying engine as Google Chrome. Anything that works in Chrome will work in Electron. Now you can adjust your CSS using the computing power of your laptop PC. Also note that for CSS changes, you don’t need to stop and start Magic Mirror. If you make a change to your custom.css file, for example, all you need to do is refresh the browser window to pick up the new changes.

      Tips for Module Developers When Writing CSS

      One thing that we haven’t talked about yet is that it doesn’t matter where your CSS is written, or how many files you have. To the browser, it’s all one big pile of styles. That means it’s entirely possible to write a CSS rule in your own module that affects the layout of another module. Here’s an example. Say you wanted to change the style of your module’s header:

      header {
        font-size: 24px;
        color: #FFCC00;
      }
      

      The problem here is that there’s nothing here to limit your CSS to just your module. This will be applied to ALL header elements in the browser.

      Luckily there’s a built-in mechanism in Magic Mirror that you can exploit to ensure your styles only apply to your own module. Automatically, the name of your module is included in the outer wrapper of your module. Say you wrote a module named MMM-Amazing-Module, you could limit the change the header to just your module as follows:

      .MMM-Amazing-Module header {
        font-size: 24px;
        color: #FFCC00;
      }
      

      What this rule is saying is any header element that is a child of an element with the MMM-Amazing-Module class will have the text sized to 24px and coloured banana yellow. All other headers will be left to their own devices.

      With that in mind, as a module developer, you should prefix all of your CSS rules with the class name of your module. This will ensure that any style you write will not accidentally affect any other module that just happens to use the same class name somewhere.

      Something I’ve already mentioned in this article deserves a second mention. When writing modules, try to avoid using inline CSS styles whenever possible. There are specific instances where this is unavoidable. For example, in the Calendar module, you can specify a colour for a given schedule. The only way this colour can be applied is using an inline style. That is acceptable, since the colour would have been user selected, and can be customized via the config file rather than in CSS. What you should avoid doing, however, is applying a default style inline. Apply a class to the element that has the colour to be applied as the default. If there is no colour specified in the config, then do not apply any inline style. The default style can be easily overridden with a more specific rule in custom.css.

      Final Thoughts

      As thourough as this guide is, it only scratches the surface of what’s possible with CSS. For example it doesn’t get into some of the more advanced selector options, like this:

      div.title > span::first-child + img:not(.logo) {
        ...
      }
      

      This will select an img element that does not have the class logo that is the immediate next sibling element of a span element that is the first immediate child of a div element with the class title. Got that? Don’t worry… this is an extremely rare example, but it illustrates the finely detailed power at your disposal.

      We didn’t get into the various positioning models, the different box models, using pseudo classes to insert arbitray content into elements or applying alternating styles to table rows. There is lot of information in this guide that needs to sink in first. For now, have fun hacking away at your Magic Mirror. Hopefully you have just enough knowledge to be dangerous!

      Have fun!

      posted in Custom CSS
      J
      j.e.f.f
    • MMM-OpenWeatherForecast - Replacement for MMM-DarkSkyForecast

      Ever since Apple bought Dark Sky and announced that that would stop allowing free access to their API (Thanks Apple!), I’ve been meaning to write a replacement for MMM-DarkSky Forecast.

      Well I finally got off my lazy ass and wrote it. Link to code here:
      https://github.com/jclarke0000/MMM-OpenWeatherForecast

      MMM-OpenWeatherForecast.png

      If you’re already using MMM-DarkSkyForecast, this will look familiar to you. In fact I based MMM-OpenWeatherForecast on MMM-DarkSkyForecast’s code.

      But! Many improvements have been included. With this rewrite I have made it possible to display every bit of weather data provided in OpenWeather’s One Call API. Be sure to fully read the README.md file to see how you can configure this module exactly to your liking.

      Installation:

      1. Navigate into your MagicMirror modules folder and execute
        git clone https://github.com/jclarke0000/MMM-OpenWeatherForecast.git.

      2. Enter the new MMM-OpenWeatherForecast directory and execute npm install.

      NOTE
      If you’re using a pre-release version, doing git pull won’t be enough to install the latest code. I replaced the deprecated request library with axios. So make sure you run npm install to get the latest dependencies. Optionally you may run npm prune to remove installation for request.

      Configuration:

      At a minimum you need to supply the following required configuration parameters:

      • apikey
      • latitude
      • longitude

      apikey needs to be specified as a String, while latitude and longitude can be specified as either a String or a Number. Both work fine.

      e.g.,

        {
          module: "MMM-OpenWeatherForecast",
          position: "top_right",
          header: "Forecast",
          config: {
            apikey: "a1b2c3d4e5f6g7h8j9k0", //only string here
            latitude: 51.490230,            //number works here
            longitude: "-0.258810"          //so does a string
          }
        },
      

      You need to create a free account with OpenWeather in order to get an API key:
      https://home.openweathermap.org/users/sign_up.

      Free tier is fine – this module will not make anywhere near 60 calls per minute / 1,000,000 requests per month.

      Find out your latitude and longitude here:
      https://www.latlong.net/.

      Refer to the README for all of the other configuration options.

      NOTE
      This module uses the Nunjucks templating system and therefore requires MagicMirror version 2.2.0 or later.

      posted in Utilities
      J
      j.e.f.f
    • MMM-MyScoreboard

      Here’s a module to display today’s scores for your favourite teams across multiple leagues.

      UPDATE 3-OCT-2022!

      • Correction of SNET provider ticker URL
      • Change of ESPN provider URL from HTTP to HTTPS
      • Replacement of deprecated library Request with Axios
      • Addition of of WNBA and NCAAW womens’ basketball
      • Team logo updates for various North American leagues
      • Correction of NCAA Wisconsin Badgers team shortcode from WIS to WISC

      Note to the anyone updating from a previous version:

      1. In the MMM-MyScoreboard directory, run git pull to get the latest source code
      2. Run npm install to install the new dependencies
      3. (Optional) Run npm prune to remove old dependencies that are no longer required

      Display is configurable for your preferred of 6 different view styles:
      0_1499122243396_MMM-MyScoreboard-viewStyles.jpg

      But Jeff! There are already modules for all these sports! Why did you write this?

      Glad you asked! While there are already modules to support many of the sports I am interested in, they all look and behave just a little bit differently. I wanted my scoreboard to have a consistent look across all leagues, and I also wanted the module to hide itself completely if there were no games.

      Furthermore, I wanted this to be modular such that adding more leagues would be fairly straightforward. Right now it supports NHL, MLB, NBA, NFL, MLS, NCAAF and CFL (Canadian Football). Pretty much any league with a public API can be added provided it fits with the two team, current score with status (e.g.: current period and game clock, future start time, etc.) format.

      So what’s the catch?

      My module ONLY displays today’s games. You won’t see yesterday’s scores (well you can with a creative hack using one of the configs… more below), nor will you see a schedule for upcoming games. My thought is you’re not really spending a lot of time looking at your mirror – you’re likely busy doing other stuff, like actually watching the game. But for today’s games that haven’t yet started, it’s a quick reminder that you need to make time later for your obligation to consume professional sport.

      What’s this magical hack you speak of?

      There is a configuration called rolloverHours that treats x amount of hours past midnight as the previous day. This way your scores don’t mysteriously disappear mid game at midnight (West coast NHL playoff overtime anybody?). Typically you might set this to 2 or 3 hours, but if you set it, say, to 12, then you’ll see yesterday’s finals up until noon, after which you’ll start to see today’s lineup.

      Enjoy this module! I’m quite proud of it, and let me know if you’d like to see some other leagues included. I’ve already had a request for pro video game leagues. It’s a little different and poses a bit of a challenge but I’ll see if it can be done.

      Special thanks to my beta testers @number1dan, @nhl, @d3r, @cowboysdude, and anyone else who tried this module out while I was getting everything working.

      -Jeff

      posted in Sport
      J
      j.e.f.f
    • RE: Rude Module Developers

      I understand the frustration felt by people who feel like they don’t have the skills to make the changes themselves. I used to be there too. But as others suggest, a community driven project like this really requires you to get you own hands dirty and be prepared to learn something new.

      JavaScript isn’t that hard as far as programming languages go, and modifying someone else’s code is the best way to dip your toe in the water. Also, Google is your friend when you get stuck.

      The above quote suggesting that user fork the code is mine. In my case, I live in the UK now, and making changes to MMM-MyScoreboard means I need to stay up really late to test any changes. Like most people here, I have a regular 9-5 job that isn’t this nor do I program for a living. So to be blunt I’m not going to make many changes to the code beyond what I use it for. Maybe that can be considered a rude response, but it was wasn’t my intent when I made that statement. I was genuinely trying to encourage him to find his own solution. There are plentiful resources available without depending on others to do the work for you.

      Respectfully, Google is your friend.

      posted in General Discussion
      J
      j.e.f.f
    • MMM-DarkSkyForecast - Yet ANOTHER weather module

      Hi folks

      UPDATE 28-FEB-2021
      This module has been replaced with MMM-OpenWeatherForecast. Apple bought Dark Sky and decided to stop allowing free access to their weather API (Thanks Apple). OpenWeatherForecast does everything this module does plus a bunch of improvements you’ve all been asking for.

      UPDATE 9-Dec-2018
      I’ve updated this module to use the Nunjucks HTML templating system, which means as of verison 1.6, this module requires MagicMirror version 2.2.0 or later. If you are running on an older version of MagicMirror, then please use v1.5 of the module, which you can download here:

      https://github.com/jclarke0000/MMM-DarkSkyForecast/releases/tag/v1.5

      Instead of using git clone to get the module code, download the zip file from the link above and extract the contents of the ZIP file to your modules directory. This zip puts all of the files into a folder named MMM-DarkSkyForecast-1.5. I don’t know if it’s necessary to rename this directory to just MMM-DarkSkyForecast or if it will work as-is. Run npm install as normal in the directory to install all of the dependencies.

      END UPDATES

      I had been using my old weather module for quite some time, but now that Weather Underground no longer allows free API access, I’ve needed a replacement.

      The great news is that there are SO many weather modules available now, but given that each of them behaved a little differently, it would have taken a considerable amount of time to go through and evaluate each one to see if it suited my needs.

      I instead decided to use that time to write one that exactly matches my needs. I’ll admit it is quite possible that another weather module does everything mine does and perhaps more, but being the lazy ass that I am, I didn’t take the time to check 🙂

      If you find it useful, then here it is.

      It supports current conditions, hourly and daily forecasts and is highly configurable. It includes a handful of icon sets and even supports Dark Sky’s Skycons animated icon set.

      If you don’t already have one, you’ll need to get a Dark Sky API key from here:
      https://darksky.net/dev

      You’ll also need the latitude and longitude of the region for which you want to display weather conditions. You can find that here:
      https://www.latlong.net/

      Screenshot:
      0_1543586344458_forecast-layouts.png

      Included Icon sets
      0_1543586376325_iconsets.png

      This does not share any code with my old MMM-MyWeather module. This is a completely new module from scratch that is a bit more organized code-wise and outputs HTML markup better suited for multiple layouts.

      posted in Utilities
      J
      j.e.f.f
    • RE: MMM-MyCommute

      MMM-MyCommute has been updated with some new configuration options

      • You can now specify hideDays with a list of days during which the module is hidden. You specify an array of numbers, 0=Sunday, 6=Saturday. To hide it on weekends, for example, specify hideDays: [0,6].

      • startTime, endTime, and hideDays parameters are now available as options for each individual destination. It should be noted that these parameters are superseded by the same ones specified in the main config. That means that if the main config parameters cause the module to be hidden as a whole, none of the destinations will be listed, regardless of their window settings.

      Thanks for all your suggestions! This has evolved into a very robust module and I’ve learned quite a bit in the process.

      Enjoy!
      -Jeff

      posted in Transport
      J
      j.e.f.f
    • RE: Details about Nunjucks templating system?

      @strawberry-3.141 I ended up just diving in and figuring it out. I spent some time rewriting MMM-MyCalendar to use the Nunjucks templating system. Here’s what I’ve learned:

      • The screen update is triggered using the updateDom() function, just like other modules
      • When updateDom() is called, so is getTemplateData() which refreshes the data model provided to the template
      • The screen often seems to physically update even if the DOM contents have not changed, which is different behaviour from modules using the old getDom() routine. I’m wondering if Nunjucks injects something into the template that makes each update distinctly different in the markup…
      • Updates on the screen seem to take a second or so longer than the old way of doing things. A screen update will result in a moment of black even if you have not specified a fade duration (i.e. updateDom() vs updateDom(200))

      Generally I really like the templating system. Makes tweaking the UI so much easier than wading through a bunch of document.createElement statements. If you’ve used Nunjucks before you’ll feel right at home and wonder what took so long for this feature to be implemented 🙂

      Just to give you an idea of what to expect, here is what the template looks like for MMM-MyCalendar now. Much less code and way easier to read.

      0_1520972259329_Screen Shot 2018-03-13 at 8.17.15 PM.png

      posted in Development
      J
      j.e.f.f
    • RE: Conditional Show of a module

      @knubbl Maybe have a look at this: https://github.com/ianperrin/MMM-ModuleScheduler

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: MMM-MyCalendar

      I worked with @PCPAYN3 over chat to figure out this issue. For anyone else this might help, the problem was as follows:

      The following error came up in the log:
      Error: dates lower than Thu Jan 01 1970 00:00:00 GMT-0500 (EST) are not supported

      A bit of Googling pointed me to this thread:
      https://forum.magicmirror.builders/topic/2208/calendar-shows-no-entries-after-mm-update/29?page=3

      Apparently the problem is that there is an entry in the calendar that has a date prior to Jan 1, 1970. The solution was to edit the following file:

      /home/pi/MagicMirror/node_modules/rrule-alt/lib/rrule.js

      and change the ORDINAL_BASE property on line 56 to an earlier date.

      example, I changed:
      ORDINAL_BASE: new Date(1970, 0, 1),

      to
      ORDINAL_BASE: new Date(1900, 0, 1),

      and then the calendar worked.

      posted in Utilities
      J
      j.e.f.f

    Latest posts made by j.e.f.f

    • RE: Pir sensor

      @tanvir586 no need to install Ext-Screen. You can use Ext-PIR on its own.

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: MMM-OpenWeatherForecast - Replacement for MMM-DarkSkyForecast

      @DDE12 Could you share your config? Also be sure put your API key in quotes in the configuration. If you aren’t using quotes, this could be a reason why the module is giving you the 401 error

      posted in Utilities
      J
      j.e.f.f
    • RE: Performance of Raspbian Bullseye on Pi 3B

      @sdetweil Yep! A rebuild on Buster made all the difference. Performance is back up to what I remember it being under Jesse.

      Thanks for the tip!

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: Pir sensor

      @tanvir586 No need for google assistant. Works fine without it.

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: Performance of Raspbian Bullseye on Pi 3B

      Also does installing Raspbian lite make a difference performance-wise or does it just save space?

      posted in Troubleshooting
      J
      j.e.f.f
    • Performance of Raspbian Bullseye on Pi 3B

      I recently rebuilt my mirror and installed Raspbian Bullseye as the base OS. I’m finding the performance to be REALLY slow… when my PIR sensor picks up motion, it takes a good 5-10 seconds or so for the mirror to respond, and when it does the transition from my screensaver module to normal layout is nowhere near smooth. Like 2-3 FPS.

      Am I expecting too much from a Pi 3B with running Bullseye? If so, what OS should I redo the build on that performs well, but is not so outdated to the point I’ll run into unsupported code problems?

      I don’t really want to purchase a Pi4 for this, as the Pi3B was at one point in time fine for this project and I’m not demanding anything more from it than I originally did.

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: Pir sensor

      If you’re still looking for a working PIR sensor module, I’m using EXT-Pir and it has been great
      https://github.com/bugsounet/EXT-Pir

      posted in Troubleshooting
      J
      j.e.f.f
    • RE: MMM-MyScoreboard

      @Wenike said in MMM-MyScoreboard:

      @j-e-f-f This can definitely be a future improvement but is there a way to get support for some of the relatively recent expansion MLS teams or if I use their codes, would it just work without additional code (especially with the ESPN update)?

      The recent update already includes all current 28 MLS teams. However any future additional teams will work provided you know the shortcode. MLS is provided by SportsNet, not ESPN, and if you can figure out the shortcode SportsNet uses for any new team, you can follow that team specifically. Otherwise, if you just follow the league, the new team will appear as it comes through the feed without any intervention on your part. I also changed MLS to pull the team logo images from the data feed, so there won’t be any missing logos either.

      posted in Sport
      J
      j.e.f.f
    • RE: MMM-MyScoreboard

      New update released. This is a big one. Addresses the following:

      • Correction of SNET provider ticker URL
      • Change of ESPN provider URL to HTTPS
      • Replacement of deprecated library Request with Axios
      • Addition of WNBA and NCAAW womens’ basketball leagues
      • Team logo updates for North American leagues
      • Correction of NCAA team shortcode for Wisconsin Badgers from WIS to WISC

      As this has a replacement of a module, you’ll need to run npm install after doing a git pull in the MMM-MyScoreBoard directory

      A note on ESPN’s feed. I have seen a number of posts reporting that the it sporadically stops working. As of right now the ESPN feed works properly for me, but I did notice over the course of the last couple of day’s testing that sometimes the request out to ESPN is not answered in a timely manner. Eventually ESPN will update, but sometimes it is slow. I have made a change so that the request is made over HTTPS instead of HTTP, and my testing since then has shown the feed to be more reliable. Of course, this could just be a complete coincidence, and it’s equally as likely that we’ll continue to see issues with ESPN-sourced scoreboard data.

      Once again, sorry for the LOOOOOOONG delay in addressing this. My mirror has sat unattended to for quite some time, and I only recently got around to rebuilding things. A number of my modules had stopped working, not just this one. Hopefully, this gets everyone back up and running.

      posted in Sport
      J
      j.e.f.f
    • RE: MMM-MyScoreboard

      From my standpoint, the only thing that stopped working for me were any of the feeds that used the Sportsnet provider. But I am seeing some anecdotal reports of the ESPN provider also not working? Could anyone provide some examples of this? ESPN is working for me as-is.

      posted in Sport
      J
      j.e.f.f