-
-
Save vladkorotnev/63a6d27a9a4426aabab7 to your computer and use it in GitHub Desktop.
radio 1.3 ~~ Internet - FM - Weather - FM Transmitter - Transliteration of track titles - Cool text effects - More to come
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from datetime import * | |
def generateTable(sunriseString='4:38 AM', sunsetString='9:58 PM'): | |
sunrise = datetime.strptime(sunriseString, '%I:%M %p') | |
sunset = datetime.strptime(sunsetString, '%I:%M %p') | |
sunriseIndex = sunrise.hour + (1 if sunrise.minute >= 30 else 0) | |
sunsetIndex = sunset.hour + (1 if sunset.minute >= 30 else 0) | |
peakIndex = sunriseIndex + 4 | |
peakEndIndex = sunsetIndex - 4 | |
if sunsetIndex > 23: | |
sunsetIndex = 23 | |
generatedTable = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] | |
for i in range(0,sunriseIndex-1): | |
generatedTable[i] = 2 | |
generatedTable[sunriseIndex-1]=3 | |
generatedTable[sunriseIndex] = 4 | |
stepping = 3.0/(peakIndex-sunriseIndex) | |
prev = 4 | |
for i in range(sunriseIndex+1,peakIndex+1): | |
prev = prev + stepping | |
generatedTable[i] = int(prev) | |
for i in range(peakIndex+1,peakEndIndex+1): | |
generatedTable[i]=7 | |
prev = 7 | |
stepping = 4.0/(sunsetIndex-peakEndIndex) | |
for i in range(peakEndIndex+1,sunsetIndex+1): | |
prev = prev - stepping | |
generatedTable[i] = int(prev) | |
for i in range(sunsetIndex+1, len(generatedTable)): | |
generatedTable[i] = 2 | |
return generatedTable | |
def printTable(tableToPrint): | |
for i in reversed(range(0,8)): | |
lout = str(i)+" " | |
for j in range(0,len(tableToPrint)): | |
if tableToPrint[j] == i: | |
lout = lout + "**" | |
elif tableToPrint[j] > i: | |
lout = lout + "||" | |
elif tableToPrint[j] < i: | |
lout = lout + " " | |
print lout | |
lout = " " | |
for i in (range(0,len(tableToPrint))): | |
sis = str(i) | |
if len(sis) < 2: | |
sis = sis+" " | |
lout = lout + sis | |
print lout |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sox -t raw -b 16 -e signed-integer -r$1 -c 2 /tmp/mpd.fifo -t wav - |sudo /pifm/PiFmRds/src/pi_fm_rds -freq $2 -ps "" -rt '' -audio - |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
/usr/local/bin/rtl_fm -g 34 -f $1 -M fm -s 192000 -r 192000 -F 0 -E deemp - | /usr/bin/aplay -r 192000 -f S16_LE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import wiringpi2 as w | |
from threading import Thread | |
import time | |
ST_BREATHE = 2 | |
ST_BLINK = 1 # Reserved | |
ST_GENERIC = 0 | |
ST_BLIHOLD = -1 #reserved | |
PW_PIN = 9 | |
YE_PIN = 10 | |
GR_PIN = 11 | |
class LED: | |
state = ST_GENERIC | |
pin = 0 | |
value = 0 | |
breathRate = 5 | |
breathFloor = 50 | |
blinkRate = 1 | |
def __init__(self, pin, initialValue=0): | |
w.wiringPiSetupGpio() | |
self.pin = pin | |
self.value = initialValue | |
w.pinMode(self.pin, 1) | |
w.softPwmCreate(self.pin,0,100) | |
self._setValue(self.value) | |
def _setValue(self, value): | |
w.softPwmWrite(self.pin, int(value)) | |
def _brightRange(self, level, src): | |
return (range(src, level) if src < level else reversed(range(level, src))) | |
def setValue(self, value, smooth=True, smoothDelay=2): | |
if self.state != ST_GENERIC: | |
self.value = value | |
self.state = ST_GENERIC | |
return | |
if smooth: | |
for i in self._brightRange(value, self.value): | |
self._setValue(i) | |
w.delay(smoothDelay) | |
self._setValue(value) | |
self.value = value | |
def _breather(self): | |
while self.state == ST_BREATHE or self.state == ST_BLIHOLD: | |
for i in self._brightRange(self.breathFloor, self.value): | |
if self.state == ST_BREATHE: | |
self._setValue(i) | |
w.delay(self.breathRate) | |
time.sleep(self.breathRate/10) | |
for i in self._brightRange(self.value, self.breathFloor): | |
if self.state == ST_BREATHE: | |
self._setValue(i) | |
w.delay(self.breathRate) | |
time.sleep(self.breathRate/10) | |
self.setValue(self.value, smooth=True) | |
def startBreathing(self, val=100, floor=15, rate=15): | |
self.breathRate = rate | |
self.breathFloor=floor | |
if self.state == ST_BREATHE: | |
self.value = val | |
return | |
self.setValue(val, True) | |
self.state = ST_BREATHE | |
Thread(target=self._breather).start() | |
def blinkOnce(self): | |
oldState = self.state | |
self.state=ST_BLIHOLD | |
if not self.value == 50: | |
self._setValue(100-self.value) | |
w.delay(50) | |
self._setValue(self.value) | |
else: | |
self._setValue(0) | |
w.delay(50) | |
self._setValue(50) | |
self.state = oldState |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Please make this file available to others | |
# by sending it to <lirc@bartelmus.de> | |
# | |
# this config file was automatically generated | |
# using lirc-0.9.0-pre1(default) on Fri May 30 11:53:01 2014 | |
# | |
# contributed by | |
# | |
# brand: /etc/lirc/lircd.conf | |
# model no. of remote control: | |
# devices being controlled by this remote: | |
# | |
begin remote | |
name /etc/lirc/lircd.conf | |
flags RAW_CODES | |
eps 30 | |
aeps 100 | |
gap 9105 | |
begin raw_codes | |
name key_wakeup | |
9128 4394 647 488 650 466 | |
673 453 683 424 664 470 | |
639 470 634 499 638 470 | |
660 475 679 429 653 485 | |
631 471 631 1638 681 426 | |
681 453 665 443 641 493 | |
635 472 642 498 661 441 | |
680 454 654 454 638 496 | |
634 474 642 1625 684 1562 | |
684 1587 683 1553 696 1571 | |
692 1550 704 1564 703 1538 | |
705 | |
name key_sat | |
9150 4426 619 487 639 469 | |
827 299 642 474 630 505 | |
625 474 641 519 604 502 | |
608 528 604 496 612 528 | |
606 501 613 1669 596 500 | |
619 522 597 553 565 523 | |
607 1644 597 526 606 503 | |
600 583 556 515 585 539 | |
597 516 591 1678 589 515 | |
592 1682 640 1598 601 1662 | |
604 1646 599 1662 603 1640 | |
599 | |
name key_audio | |
9141 4391 628 520 610 498 | |
601 748 386 523 583 554 | |
586 499 644 497 596 504 | |
604 530 601 505 603 532 | |
603 521 587 1667 600 505 | |
603 531 605 505 651 1619 | |
600 506 644 1815 413 1641 | |
632 499 600 505 603 532 | |
600 509 600 545 587 1730 | |
644 400 603 506 596 1683 | |
592 1646 594 1681 585 1650 | |
597 | |
name key_mute | |
9119 4420 623 502 626 481 | |
627 507 628 480 628 505 | |
628 480 618 517 613 495 | |
606 527 611 497 608 526 | |
605 503 605 1663 609 500 | |
608 524 602 507 602 532 | |
600 507 601 534 599 1642 | |
601 1672 596 507 600 534 | |
598 510 600 1668 601 1644 | |
611 1659 596 507 599 534 | |
599 1659 581 1669 603 1639 | |
611 | |
name key_1 | |
9140 4399 609 520 610 500 | |
607 527 604 504 604 529 | |
604 506 631 501 602 556 | |
559 525 604 504 607 526 | |
608 506 603 1704 567 535 | |
571 526 605 504 597 1670 | |
605 1642 604 524 631 483 | |
594 591 545 519 577 562 | |
572 521 641 497 580 520 | |
592 1685 573 1678 572 1681 | |
588 1657 579 1685 596 1650 | |
588 | |
name key_2 | |
9124 4427 611 515 638 478 | |
599 539 601 508 595 537 | |
630 479 591 538 596 507 | |
601 541 593 506 598 537 | |
598 511 599 1843 434 500 | |
586 549 590 516 595 540 | |
589 519 583 1682 588 524 | |
591 602 528 522 579 559 | |
571 531 582 1694 570 1665 | |
583 553 572 1691 552 1695 | |
579 1658 581 1692 575 1664 | |
581 | |
name key_3 | |
9207 4312 611 567 562 489 | |
613 521 651 458 602 547 | |
593 498 607 531 621 485 | |
604 549 749 366 577 535 | |
600 503 605 1693 575 503 | |
608 525 617 494 621 1650 | |
595 513 586 1682 600 507 | |
602 528 601 511 579 552 | |
591 522 574 575 567 1667 | |
577 547 580 1666 576 1687 | |
586 1658 579 1680 597 1649 | |
588 | |
name key_4 | |
9101 4429 609 525 609 497 | |
608 527 603 504 603 531 | |
603 506 601 532 601 508 | |
601 532 632 476 607 527 | |
602 506 599 1669 596 513 | |
597 536 599 511 592 540 | |
588 1661 580 1682 594 513 | |
591 542 584 525 579 560 | |
580 528 574 1702 566 528 | |
577 557 577 1665 580 1690 | |
576 1666 575 1690 581 1663 | |
576 | |
name key_5 | |
9116 4424 608 536 595 498 | |
609 525 607 506 599 532 | |
603 511 698 447 586 503 | |
601 533 601 504 652 482 | |
608 505 607 1660 602 504 | |
607 526 600 510 646 1627 | |
596 1640 612 1660 595 508 | |
601 533 596 515 599 533 | |
602 574 529 540 590 647 | |
455 548 593 1653 585 1776 | |
488 1657 582 1680 603 1646 | |
589 | |
name key_6 | |
9115 4418 645 488 622 487 | |
614 521 606 501 607 527 | |
607 502 608 525 605 503 | |
602 534 604 503 600 534 | |
601 506 601 1667 599 507 | |
601 535 599 508 599 536 | |
603 504 599 536 592 1648 | |
591 545 593 514 596 538 | |
596 512 590 1684 584 1651 | |
597 1673 593 535 563 1713 | |
566 1667 570 1702 562 1677 | |
574 | |
name key_7 | |
9111 4430 608 526 606 502 | |
606 529 603 503 604 531 | |
607 501 602 533 601 507 | |
600 533 601 507 601 534 | |
598 510 600 1667 606 502 | |
601 534 591 517 590 1676 | |
594 515 595 543 588 1659 | |
583 544 582 524 583 552 | |
583 525 580 552 580 1664 | |
577 1689 582 528 579 1687 | |
579 1662 581 1687 583 1664 | |
576 | |
name key_8 | |
9126 4434 600 532 610 516 | |
574 539 610 484 612 524 | |
613 497 602 534 596 512 | |
601 534 610 496 601 541 | |
587 619 480 1689 577 524 | |
595 540 597 512 587 610 | |
516 1668 591 532 591 1663 | |
580 550 600 505 586 548 | |
581 527 612 1638 602 516 | |
593 1662 650 455 614 1657 | |
602 1641 606 1653 647 1605 | |
600 | |
name key_9 | |
9111 4402 658 467 683 475 | |
580 507 626 503 588 547 | |
601 504 607 541 591 517 | |
579 575 579 508 571 564 | |
587 520 686 1611 553 523 | |
579 556 581 526 585 1684 | |
797 1445 582 553 575 1663 | |
584 550 578 527 586 552 | |
574 533 583 549 581 526 | |
582 1688 585 525 580 1682 | |
587 1657 604 1661 691 1569 | |
635 | |
name key_0 | |
9116 4412 632 497 626 482 | |
634 502 624 481 629 512 | |
626 477 621 513 617 490 | |
609 525 618 490 608 525 | |
612 517 590 1657 616 492 | |
604 531 608 498 609 524 | |
603 506 600 1667 602 1641 | |
610 544 581 526 580 557 | |
578 530 577 1673 597 1662 | |
577 557 585 523 578 1669 | |
601 1640 600 1676 604 1637 | |
596 | |
name key_red | |
9109 4440 598 526 620 485 | |
608 535 592 544 564 529 | |
610 500 659 482 600 505 | |
601 534 599 528 583 531 | |
598 510 599 1680 594 506 | |
600 533 601 588 522 533 | |
599 509 594 1674 598 1643 | |
600 1664 600 512 590 544 | |
594 513 597 1671 602 1640 | |
597 537 587 520 612 523 | |
592 1651 589 1673 595 1652 | |
590 | |
name key_green | |
9119 4409 629 504 631 477 | |
627 507 627 483 620 513 | |
627 481 618 515 614 500 | |
600 530 601 504 612 521 | |
608 501 605 1671 595 507 | |
602 536 597 514 593 1665 | |
603 506 600 1667 603 1639 | |
605 1668 597 506 602 532 | |
601 507 601 533 600 1642 | |
599 538 597 508 599 542 | |
592 1641 599 1669 600 1642 | |
600 | |
name key_yellow | |
9128 4419 609 530 606 497 | |
608 526 605 502 605 530 | |
604 504 603 532 602 505 | |
602 533 601 507 600 531 | |
604 507 599 1667 601 514 | |
594 535 598 507 600 534 | |
600 1644 596 538 597 1644 | |
594 1689 578 534 569 547 | |
589 520 578 1690 581 526 | |
581 1693 573 530 574 580 | |
555 1671 576 1686 577 1665 | |
579 | |
name key_blue | |
9117 4432 607 525 608 500 | |
604 533 601 504 605 529 | |
603 505 601 533 601 508 | |
601 532 600 507 600 533 | |
607 502 600 1668 600 507 | |
599 536 600 508 598 536 | |
593 1648 597 1671 598 1645 | |
593 1675 593 514 596 538 | |
596 511 593 1676 586 522 | |
588 546 590 518 588 545 | |
590 1651 584 1690 579 1657 | |
588 | |
name key_time | |
9120 4427 608 525 611 498 | |
605 534 602 500 605 532 | |
603 503 601 536 600 504 | |
603 532 603 504 603 530 | |
604 505 603 1669 598 507 | |
608 528 598 507 601 1677 | |
583 517 592 543 592 1650 | |
592 1678 583 521 591 542 | |
590 518 592 541 588 1658 | |
578 1689 586 519 595 541 | |
580 1669 572 1686 592 1650 | |
589 | |
name key_exit | |
9124 4379 652 483 645 461 | |
646 488 639 474 631 498 | |
634 474 629 505 631 477 | |
630 505 628 479 629 506 | |
627 481 627 1640 629 481 | |
625 510 624 485 623 531 | |
603 1637 604 1668 601 505 | |
601 1667 602 506 601 532 | |
602 505 602 1668 601 507 | |
600 533 601 1641 601 533 | |
602 1640 600 1669 605 1636 | |
600 | |
name key_left | |
9127 4416 611 520 615 494 | |
609 528 602 503 611 525 | |
603 505 602 531 603 505 | |
602 532 602 506 604 529 | |
603 506 601 1673 597 506 | |
600 534 602 505 607 542 | |
586 1640 601 534 600 514 | |
593 1668 601 509 598 535 | |
599 510 590 1676 596 511 | |
600 1674 585 1653 594 539 | |
597 1645 598 1670 585 1661 | |
592 | |
name key_up | |
9114 4439 601 526 605 502 | |
605 530 603 505 603 530 | |
603 506 601 533 601 512 | |
596 532 622 485 602 532 | |
601 507 602 1666 601 508 | |
600 533 601 506 601 1669 | |
595 512 598 536 602 510 | |
594 1673 587 519 583 550 | |
588 520 589 546 589 1650 | |
587 1682 588 1657 598 534 | |
585 1656 583 1685 589 1652 | |
587 | |
name key_down | |
9120 4405 629 520 614 489 | |
616 583 549 498 607 528 | |
606 503 630 510 599 516 | |
593 600 530 506 600 535 | |
600 520 589 1673 595 504 | |
605 530 608 502 609 1665 | |
594 503 625 1679 569 508 | |
604 1690 576 506 606 526 | |
601 508 606 528 607 1640 | |
600 530 604 1652 594 523 | |
605 1644 603 1662 596 1626 | |
594 | |
name key_right | |
9117 4424 619 518 612 497 | |
606 537 599 504 600 530 | |
606 501 613 522 603 504 | |
602 532 602 506 603 531 | |
602 506 601 1665 603 506 | |
603 532 601 506 601 533 | |
601 509 600 1665 602 508 | |
600 1673 595 507 599 535 | |
600 506 602 1667 601 1642 | |
600 533 600 1644 599 534 | |
600 1642 591 1675 601 1641 | |
599 | |
name key_ok | |
9114 4430 609 524 604 504 | |
604 530 603 504 602 537 | |
597 506 628 505 602 508 | |
600 533 600 508 600 534 | |
600 508 599 1668 596 513 | |
597 537 596 512 598 1670 | |
593 1647 593 543 594 519 | |
586 1676 586 522 584 550 | |
583 524 587 548 590 518 | |
580 1692 572 1666 584 579 | |
554 1659 580 1687 584 1662 | |
583 | |
name key_menu | |
9103 4435 602 550 579 498 | |
603 579 552 510 599 532 | |
605 504 600 533 599 571 | |
565 507 600 506 602 535 | |
601 505 653 1626 591 508 | |
595 536 600 509 602 530 | |
604 1663 622 1630 639 1600 | |
598 530 723 404 570 549 | |
586 521 594 1696 574 509 | |
596 543 586 518 590 1678 | |
595 1650 587 1686 645 1592 | |
587 | |
name key_volumeup | |
9131 4474 562 477 645 464 | |
639 495 636 471 638 498 | |
635 472 631 539 595 478 | |
628 505 628 483 627 503 | |
629 482 624 1737 533 480 | |
627 507 627 488 624 1650 | |
617 1624 612 1651 621 1617 | |
625 1644 622 481 625 510 | |
692 432 613 527 603 505 | |
609 524 615 493 610 526 | |
739 1506 607 1660 600 1645 | |
606 | |
name key_volumedown | |
9109 4411 625 511 612 498 | |
615 519 614 500 612 522 | |
613 498 597 534 605 531 | |
572 536 599 510 596 538 | |
594 507 601 1680 662 434 | |
603 538 594 659 516 464 | |
605 505 600 532 603 507 | |
599 570 566 505 601 1664 | |
603 527 579 1672 598 1644 | |
598 1672 594 1651 595 1679 | |
587 1648 594 530 601 1652 | |
593 | |
name key_next | |
9118 4399 641 518 615 465 | |
643 601 531 462 639 502 | |
630 480 626 506 627 485 | |
628 501 629 614 501 504 | |
625 491 662 1603 616 479 | |
628 504 631 473 637 507 | |
622 508 627 474 637 476 | |
627 1676 600 477 624 539 | |
596 508 598 1697 572 1644 | |
632 1633 607 1619 625 528 | |
605 1635 607 1637 641 1625 | |
605 | |
name key_previous | |
9107 4429 619 514 610 492 | |
606 528 603 505 619 534 | |
582 508 602 531 655 455 | |
607 528 598 504 602 535 | |
598 508 604 1670 700 404 | |
595 539 597 507 606 1668 | |
593 1646 597 1674 591 1657 | |
593 530 592 521 587 544 | |
586 552 564 564 571 514 | |
582 556 582 523 577 1694 | |
583 1657 581 1691 580 1705 | |
542 | |
name key_help | |
9139 4389 629 500 628 476 | |
630 505 629 478 627 505 | |
623 489 623 514 614 496 | |
612 517 611 502 612 522 | |
609 501 598 1666 610 503 | |
594 539 593 514 596 1673 | |
595 1651 592 1691 585 501 | |
595 1676 601 503 634 500 | |
597 511 597 538 595 533 | |
568 568 570 1658 588 595 | |
536 1669 573 1675 594 1666 | |
575 | |
name key_epg | |
9104 4405 633 491 633 496 | |
611 522 607 501 635 678 | |
432 500 604 521 621 496 | |
601 535 600 504 608 528 | |
601 506 633 1640 601 504 | |
605 570 563 506 603 1663 | |
614 1625 606 526 628 1616 | |
608 1662 606 500 607 528 | |
603 504 607 526 606 506 | |
603 1662 610 513 579 538 | |
604 1642 590 1673 596 1651 | |
603 | |
name key_sysrq | |
9123 4383 651 483 645 459 | |
641 494 637 501 604 500 | |
629 484 630 498 630 499 | |
653 501 597 479 626 651 | |
486 500 598 1666 607 506 | |
598 558 581 506 588 1675 | |
608 504 598 535 598 512 | |
595 538 597 506 601 1669 | |
598 504 600 531 600 1647 | |
597 1693 577 1647 600 1670 | |
603 1638 596 538 640 1604 | |
594 | |
end raw_codes | |
end remote |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# This is a modded script from init.d!!! | |
# Will start radio.py on boot | |
### BEGIN INIT INFO | |
# Provides: mpd | |
# Required-Start: $local_fs $remote_fs | |
# Required-Stop: $local_fs $remote_fs | |
# Should-Start: autofs $network $named alsa-utils pulseaudio | |
# Should-Stop: autofs $network $named alsa-utils pulseaudio | |
# Default-Start: 2 3 4 5 | |
# Default-Stop: 0 1 6 | |
# Short-Description: Music Player Daemon | |
# Description: Start the Music Player Daemon (MPD) service | |
# for network access to the local audio queue. | |
### END INIT INFO | |
. /lib/lsb/init-functions | |
PATH=/sbin:/bin:/usr/sbin:/usr/bin | |
NAME=mpd | |
DESC="Music Player Daemon" | |
DAEMON=/usr/bin/mpd | |
MPDCONF=/etc/mpd.conf | |
START_MPD=true | |
# Exit if the package is not installed | |
[ -x "$DAEMON" ] || exit 0 | |
# Read configuration variable file if it is present | |
[ -r /etc/default/$NAME ] && . /etc/default/$NAME | |
if [ -n "$MPD_DEBUG" ]; then | |
set -x | |
MPD_OPTS=--verbose | |
fi | |
if [ ! -d "/var/run/mpd" ]; then | |
mkdir /var/run/mpd | |
if dpkg-statoverride --list --quiet /var/run/mpd > /dev/null; then | |
#if dpkg-statoverride is used update it with permissions there | |
dpkg-statoverride --force --quiet --update --add $( dpkg-statoverride --list --quiet /var/run/mpd ) 2> /dev/null | |
else | |
#use defaults | |
chown mpd:audio /var/run/mpd | |
chmod 755 /var/run/mpd | |
fi | |
fi | |
DBFILE=$(sed -n 's/^[[:space:]]*db_file[[:space:]]*"\?\([^"]*\)\"\?/\1/p' $MPDCONF) | |
PIDFILE=$(sed -n 's/^[[:space:]]*pid_file[[:space:]]*"\?\([^"]*\)\"\?/\1/p' $MPDCONF) | |
mpd_start () { | |
if [ "$START_MPD" != "true" ]; then | |
log_action_msg "Not starting MPD: disabled by /etc/default/$NAME". | |
exit 0 | |
fi | |
log_daemon_msg "Starting $DESC" "$NAME" | |
if [ -z "$PIDFILE" -o -z "$DBFILE" ]; then | |
log_failure_msg \ | |
"$MPDCONF must have db_file and pid_file set; cannot start daemon." | |
exit 1 | |
fi | |
PIDDIR=$(dirname "$PIDFILE") | |
if [ ! -d "$PIDDIR" ]; then | |
mkdir -m 0755 $PIDDIR | |
chown mpd:audio $PIDDIR | |
fi | |
start-stop-daemon --start --quiet --oknodo --pidfile "$PIDFILE" \ | |
--exec "$DAEMON" -- $MPD_OPTS "$MPDCONF" | |
python /radio.py & | |
log_end_msg $? | |
} | |
mpd_stop () { | |
if [ "$START_MPD" != "true" ]; then | |
log_failure_msg "Not stopping MPD: disabled by /etc/default/$NAME". | |
exit 0 | |
fi | |
if [ -z "$PIDFILE" ]; then | |
log_failure_msg \ | |
"$MPDCONF must have pid_file set; cannot stop daemon." | |
exit 1 | |
fi | |
log_daemon_msg "Stopping $DESC" "$NAME" | |
start-stop-daemon --stop --quiet --oknodo --retry 5 --pidfile "$PIDFILE" \ | |
--exec $DAEMON | |
log_end_msg $? | |
} | |
# note to self: don't call the non-standard args for this in | |
# {post,pre}{inst,rm} scripts since users are not forced to upgrade | |
# /etc/init.d/mpd when mpd is updated | |
case "$1" in | |
start) | |
mpd_start | |
;; | |
stop) | |
mpd_stop | |
;; | |
status) | |
status_of_proc -p $PIDFILE $DAEMON $NAME | |
;; | |
restart|force-reload) | |
mpd_stop | |
mpd_start | |
;; | |
force-start) | |
mpd_start | |
;; | |
force-restart) | |
mpd_stop | |
mpd_start | |
;; | |
force-reload) | |
mpd_stop | |
mpd_start | |
;; | |
*) | |
echo "Usage: $0 {start|start-create-db|stop|restart|force-reload}" | |
exit 2 | |
;; | |
esac |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# An example configuration file for MPD | |
# See the mpd.conf man page for a more detailed description of each parameter. | |
# Files and directories ####################################################### | |
# | |
# This setting controls the top directory which MPD will search to discover the | |
# available audio files and add them to the daemon's online database. This | |
# setting defaults to the XDG directory, otherwise the music directory will be | |
# be disabled and audio files will only be accepted over ipc socket (using | |
# file:// protocol) or streaming files over an accepted protocol. | |
# | |
music_directory "/var/lib/mpd/music" | |
# | |
# This setting sets the MPD internal playlist directory. The purpose of this | |
# directory is storage for playlists created by MPD. The server will use | |
# playlist files not created by the server but only if they are in the MPD | |
# format. This setting defaults to playlist saving being disabled. | |
# | |
playlist_directory "/var/lib/mpd/playlists" | |
# | |
# This setting sets the location of the MPD database. This file is used to | |
# load the database at server start up and store the database while the | |
# server is not up. This setting defaults to disabled which will allow | |
# MPD to accept files over ipc socket (using file:// protocol) or streaming | |
# files over an accepted protocol. | |
# | |
db_file "/var/lib/mpd/tag_cache" | |
# | |
# These settings are the locations for the daemon log files for the daemon. | |
# These logs are great for troubleshooting, depending on your log_level | |
# settings. | |
# | |
# The special value "syslog" makes MPD use the local syslog daemon. This | |
# setting defaults to logging to syslog, otherwise logging is disabled. | |
# | |
log_file "/var/log/mpd/mpd.log" | |
# | |
# This setting sets the location of the file which stores the process ID | |
# for use of mpd --kill and some init scripts. This setting is disabled by | |
# default and the pid file will not be stored. | |
# | |
pid_file "/var/run/mpd/pid" | |
# | |
# This setting sets the location of the file which contains information about | |
# most variables to get MPD back into the same general shape it was in before | |
# it was brought down. This setting is disabled by default and the server | |
# state will be reset on server start up. | |
# | |
state_file "/var/lib/mpd/state" | |
# | |
# The location of the sticker database. This is a database which | |
# manages dynamic information attached to songs. | |
# | |
sticker_file "/var/lib/mpd/sticker.sql" | |
# | |
############################################################################### | |
# General music daemon options ################################################ | |
# | |
# This setting specifies the user that MPD will run as. MPD should never run as | |
# root and you may use this setting to make MPD change its user ID after | |
# initialization. This setting is disabled by default and MPD is run as the | |
# current user. | |
# | |
user "mpd" | |
# | |
# This setting specifies the group that MPD will run as. If not specified | |
# primary group of user specified with "user" setting will be used (if set). | |
# This is useful if MPD needs to be a member of group such as "audio" to | |
# have permission to use sound card. | |
# | |
#group "nogroup" | |
# | |
# This setting sets the address for the daemon to listen on. Careful attention | |
# should be paid if this is assigned to anything other then the default, any. | |
# This setting can deny access to control of the daemon. Choose any if you want | |
# to have mpd listen on every address | |
# | |
# For network | |
bind_to_address "localhost" | |
# | |
# And for Unix Socket | |
#bind_to_address "/var/run/mpd/socket" | |
# | |
# This setting is the TCP port that is desired for the daemon to get assigned | |
# to. | |
# | |
#port "6600" | |
# | |
# This setting controls the type of information which is logged. Available | |
# setting arguments are "default", "secure" or "verbose". The "verbose" setting | |
# argument is recommended for troubleshooting, though can quickly stretch | |
# available resources on limited hardware storage. | |
# | |
#log_level "default" | |
# | |
# If you have a problem with your MP3s ending abruptly it is recommended that | |
# you set this argument to "no" to attempt to fix the problem. If this solves | |
# the problem, it is highly recommended to fix the MP3 files with vbrfix | |
# (available as vbrfix in the debian archive), at which | |
# point gapless MP3 playback can be enabled. | |
# | |
#gapless_mp3_playback "yes" | |
# | |
# This setting enables MPD to create playlists in a format usable by other | |
# music players. | |
# | |
#save_absolute_paths_in_playlists "no" | |
# | |
# This setting defines a list of tag types that will be extracted during the | |
# audio file discovery process. Optionally, 'comment' can be added to this | |
# list. | |
# | |
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc" | |
# | |
# This setting enables automatic update of MPD's database when files in | |
# music_directory are changed. | |
# | |
#auto_update "yes" | |
# | |
# Limit the depth of the directories being watched, 0 means only watch | |
# the music directory itself. There is no limit by default. | |
# | |
#auto_update_depth "3" | |
# | |
############################################################################### | |
# Symbolic link behavior ###################################################### | |
# | |
# If this setting is set to "yes", MPD will discover audio files by following | |
# symbolic links outside of the configured music_directory. | |
# | |
#follow_outside_symlinks "yes" | |
# | |
# If this setting is set to "yes", MPD will discover audio files by following | |
# symbolic links inside of the configured music_directory. | |
# | |
#follow_inside_symlinks "yes" | |
# | |
############################################################################### | |
# Zeroconf / Avahi Service Discovery ########################################## | |
# | |
# If this setting is set to "yes", service information will be published with | |
# Zeroconf / Avahi. | |
# | |
#zeroconf_enabled "yes" | |
# | |
# The argument to this setting will be the Zeroconf / Avahi unique name for | |
# this MPD server on the network. | |
# | |
#zeroconf_name "Music Player" | |
# | |
############################################################################### | |
# Permissions ################################################################# | |
# | |
# If this setting is set, MPD will require password authorization. The password | |
# can setting can be specified multiple times for different password profiles. | |
# | |
#password "password@read,add,control,admin" | |
# | |
# This setting specifies the permissions a user has who has not yet logged in. | |
# | |
#default_permissions "read,add,control,admin" | |
# | |
############################################################################### | |
# Input ####################################################################### | |
# | |
input { | |
plugin "curl" | |
proxy "127.0.0.1:8080" | |
# proxy_user "user" | |
# proxy_password "password" | |
} | |
# | |
############################################################################### | |
# Audio Output ################################################################ | |
# | |
# MPD supports various audio output types, as well as playing through multiple | |
# audio outputs at the same time, through multiple audio_output settings | |
# blocks. Setting this block is optional, though the server will only attempt | |
# autodetection for one sound card. | |
# | |
# See <http://mpd.wikia.com/wiki/Configuration#Audio_Outputs> for examples of | |
# other audio outputs. | |
# | |
# An example of an ALSA output: | |
# | |
audio_output { | |
type "alsa" | |
name "My ALSA Device" | |
device "hw:0,0" # optional | |
format "44100:16:2" # optional | |
mixer_device "default" # optional | |
mixer_control "PCM" # optional | |
mixer_index "0" # optional | |
} | |
# | |
# An example of an OSS output: | |
# | |
#audio_output { | |
# type "oss" | |
# name "My OSS Device" | |
# device "/dev/dsp" # optional | |
# format "44100:16:2" # optional | |
# mixer_device "/dev/mixer" # optional | |
# mixer_control "PCM" # optional | |
#} | |
# | |
# An example of a shout output (for streaming to Icecast): | |
# | |
#audio_output { | |
# type "shout" | |
# encoding "ogg" # optional | |
# name "My Shout Stream" | |
# host "localhost" | |
# port "8000" | |
# mount "/mpd.ogg" | |
# password "hackme" | |
# quality "5.0" | |
# bitrate "128" | |
# format "44100:16:1" | |
# protocol "icecast2" # optional | |
# user "source" # optional | |
# description "My Stream Description" # optional | |
# genre "jazz" # optional | |
# public "no" # optional | |
# timeout "2" # optional | |
#} | |
# | |
# An example of a recorder output: | |
# | |
#audio_output { | |
# type "recorder" | |
# name "My recorder" | |
# encoder "vorbis" # optional, vorbis or lame | |
# path "/var/lib/mpd/recorder/mpd.ogg" | |
## quality "5.0" # do not define if bitrate is defined | |
# bitrate "128" # do not define if quality is defined | |
# format "44100:16:1" | |
#} | |
# | |
# An example of a httpd output (built-in HTTP streaming server): | |
# | |
#audio_output { | |
# type "httpd" | |
# name "My HTTP Stream" | |
# encoder "vorbis" # optional, vorbis or lame | |
# port "8000" | |
# quality "5.0" # do not define if bitrate is defined | |
# bitrate "128" # do not define if quality is defined | |
# format "44100:16:1" | |
#} | |
# | |
# An example of a pulseaudio output (streaming to a remote pulseaudio server) | |
# | |
#audio_output { | |
# type "pulse" | |
# name "My Pulse Output" | |
# server "remote_server" # optional | |
# sink "remote_server_sink" # optional | |
#} | |
# | |
## Example "pipe" output: | |
# | |
#audio_output { | |
# type "pipe" | |
# name "my pipe" | |
# command "aplay -f cd 2>/dev/null" | |
## Or if you're want to use AudioCompress | |
# command "AudioCompress -m | aplay -f cd 2>/dev/null" | |
## Or to send raw PCM stream through PCM: | |
# command "nc example.org 8765" | |
# format "44100:16:2" | |
#} | |
# | |
## An example of a null output (for no audio output): | |
# | |
#audio_output { | |
# type "null" | |
# name "My Null Output" | |
#} | |
audio_output { | |
type "fifo" | |
name "FM_fifo" | |
path "/tmp/mpd.fifo" | |
format "44100:16:2" | |
} | |
# | |
# This setting will change all decoded audio to be converted to the specified | |
# format before being passed to the audio outputs. By default, this setting is | |
# disabled. | |
# | |
#audio_output_format "44100:16:2" | |
# | |
# If MPD has been compiled with libsamplerate support, this setting specifies | |
# the sample rate converter to use. Possible values can be found in the | |
# mpd.conf man page or the libsamplerate documentation. By default, this is | |
# setting is disabled. | |
# | |
#samplerate_converter "Fastest Sinc Interpolator" | |
# | |
############################################################################### | |
# Volume control mixer ######################################################## | |
# | |
# These are the global volume control settings. By default, this setting will | |
# be detected to the available audio output device, with preference going to | |
# hardware mixing. Hardware and software mixers for individual audio_output | |
# sections cannot yet be mixed. | |
# | |
# An example for controlling an ALSA, OSS or Pulseaudio mixer; If this | |
# setting is used other sound applications will be affected by the volume | |
# being controlled by MPD. | |
# | |
#mixer_type "hardware" | |
# | |
# An example for controlling all mixers through software. This will control | |
# all controls, even if the mixer is not supported by the device and will not | |
# affect any other sound producing applications. | |
# | |
#mixer_type "software" | |
# | |
# This example will not allow MPD to touch the mixer at all and will disable | |
# all volume controls. | |
# | |
#mixer_type "disabled" | |
# | |
############################################################################### | |
# Normalization automatic volume adjustments ################################## | |
# | |
# This setting specifies the type of ReplayGain to use. This setting can have | |
# the argument "album" or "track". See <http://www.replaygain.org> for more | |
# details. This setting is disabled by default. | |
# | |
#replaygain "album" | |
# | |
# This setting sets the pre-amp used for files that have ReplayGain tags. By | |
# default this setting is disabled. | |
# | |
#replaygain_preamp "0" | |
# | |
# This setting enables on-the-fly normalization volume adjustment. This will | |
# result in the volume of all playing audio to be adjusted so the output has | |
# equal "loudness". This setting is disabled by default. | |
# | |
#volume_normalization "no" | |
# | |
############################################################################### | |
# MPD Internal Buffering ###################################################### | |
# | |
# This setting adjusts the size of internal decoded audio buffering. Changing | |
# this may have undesired effects. Don't change this if you don't know what you | |
# are doing. | |
# | |
#audio_buffer_size "2048" | |
# | |
# This setting controls the percentage of the buffer which is filled before | |
# beginning to play. Increasing this reduces the chance of audio file skipping, | |
# at the cost of increased time prior to audio playback. | |
# | |
#buffer_before_play "10%" | |
# | |
############################################################################### | |
# Resource Limitations ######################################################## | |
# | |
# These settings are various limitations to prevent MPD from using too many | |
# resources. Generally, these settings should be minimized to prevent security | |
# risks, depending on the operating resources. | |
# | |
#connection_timeout "60" | |
#max_connections "10" | |
#max_playlist_length "16384" | |
#max_command_list_size "2048" | |
#max_output_buffer_size "8192" | |
# | |
############################################################################### | |
# Character Encoding ########################################################## | |
# | |
# If file or directory names do not display correctly for your locale then you | |
# may need to modify this setting. After modification of this setting mpd | |
# --create-db must be run to change the database. | |
# | |
filesystem_charset "UTF-8" | |
# | |
# This setting controls the encoding that ID3v1 tags should be converted from. | |
# | |
id3v1_encoding "UTF-8" | |
# | |
############################################################################### | |
# SIDPlay decoder ############################################################# | |
# | |
# songlength_database: | |
# Location of your songlengths file, as distributed with the HVSC. | |
# The sidplay plugin checks this for matching MD5 fingerprints. | |
# See http://www.c64.org/HVSC/DOCUMENTS/Songlengths.faq | |
# | |
# default_songlength: | |
# This is the default playing time in seconds for songs not in the | |
# songlength database, or in case you're not using a database. | |
# A value of 0 means play indefinitely. | |
# | |
# filter: | |
# Turns the SID filter emulation on or off. | |
# | |
#decoder { | |
# plugin "sidplay" | |
# songlength_database "/media/C64Music/DOCUMENTS/Songlengths.txt" | |
# default_songlength "120" | |
# filter "true" | |
#} | |
# | |
############################################################################### | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
begin | |
prog = radio | |
button = key_wakeup | |
config = pwron | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_UP | |
config = up | |
repeat = 1 | |
end | |
begin | |
prog = radio | |
button = KEY_DOWN | |
config = down | |
repeat = 1 | |
end | |
begin | |
prog = radio | |
button = KEY_LEFT | |
config = left | |
repeat = 1 | |
end | |
begin | |
prog = radio | |
button = KEY_RIGHT | |
config = right | |
repeat = 1 | |
end | |
begin | |
prog = radio | |
button = KEY_MENU | |
config = menu | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_TIME | |
config = time | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_RED | |
config = red | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_GREEN | |
config = green | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_YELLOW | |
config = yellow | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_BLUE | |
config = blue | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_EXIT | |
config = exit | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_OK | |
config = ok | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_HELP | |
config = help | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_PREVIOUS | |
config = prev | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_NEXT | |
config = next | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_MUTE | |
config = mute | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_VOLUMEUP | |
config = volup | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_VOLUMEDOWN | |
config = voldn | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_SYSRQ | |
config = rcl | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_0 | |
config = 0 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_1 | |
config = 1 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_2 | |
config = 2 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_3 | |
config = 3 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_4 | |
config = 4 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_5 | |
config = 5 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_6 | |
config = 6 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_7 | |
config = 7 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_8 | |
config = 8 | |
repeat = 0 | |
end | |
begin | |
prog = radio | |
button = KEY_9 | |
config = 9 | |
repeat = 0 | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
from adaptive_gen import * | |
import wiringpi2 as w # For LED control | |
import pywapi as weather # for weather | |
import smbus, time, datetime, pdb, os,sys, alsaaudio, subprocess, lirc, atexit, signal, audioop,errno,math, re, threading, copy # For the rest | |
import pykakasi.kakasi as kakasi | |
from leds import * | |
from mpd import MPDClient # For media control | |
global cli, bus, mix, remote | |
from shairportdecoder.decoder import Processor | |
from shairportdecoder.metadata import Infos | |
from shairportdecoder import decoder | |
mustEnterAirport = False | |
airportInfoLine = u"" | |
airportInfoUpdate = datetime.datetime.now() | |
airportInfoChanged = False | |
airportIgnoreNextState = False | |
def AirportGoes(event_type, info): | |
global mustEnterAirport | |
global airportIgnoreNextState | |
global globalBreaker | |
if event_type == decoder.CONNECTED: | |
#print("PBEG oelutz") | |
mustEnterAirport = True | |
globalBreaker = True | |
airportIgnoreNextState = True | |
elif event_type == decoder.STATE: | |
if not (info.playstate == Infos.PLAYING) and not airportIgnoreNextState: | |
mustEnterAirport = False | |
elif (info.playstate == Infos.PLAYING): | |
airportIgnoreNextState = True #workaround for a bug in api | |
elif airportIgnoreNextState: | |
airportIgnoreNextState = False | |
def AirportEvent(event_type, info): | |
global airportInfoLine | |
global airportInfoUpdate | |
global airportInfoChanged | |
assert(isinstance(info, Infos)) | |
if event_type == decoder.VOLUME: | |
pass | |
elif event_type == decoder.COVERART: | |
pass | |
elif event_type == decoder.META: | |
if info.itemname is not None: | |
airportInfoLineTemp = japToAscii((info.songartist + " - " if info.songartist else "") + (info.itemname if info.itemname else "Unknown Track")) | |
if not (airportInfoLine == airportInfoLineTemp): | |
airportInfoLine = airportInfoLineTemp | |
#print airportInfoLine | |
airportInfoChanged = True | |
airportInfoUpdate = datetime.datetime.now() | |
#print "New meta" | |
#print "NAMELEN", len(info.itemname) | |
#print("Got Metadata,\n{meata}".format(meata=airportInfoLine)) # lol, meat typo. | |
elif event_type == decoder.STATE: | |
#print("State changed to {sas}".format(sas=info.playstate)) | |
pass | |
kakasi = kakasi() | |
kakasi.setMode("H","a") # default: Hiragana no convert | |
kakasi.setMode("K","a") # default: Katakana no convert | |
kakasi.setMode("J","a") # default: Japanese no convert | |
kakasi.setMode("E","a") # default: Symbols no convert | |
kakasi.setMode("r","Hepburn") # default: use Hepburn Roman table | |
kakasi.setMode("s", True) # separate, default: no Separator | |
kakasi.setMode("C", True) # capitalize default: no Capitalize | |
kakaconv = kakasi.getConverter() | |
kakare = re.compile(ur"([\u3000-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\uFF00-\uFFEF]|[\u4E00-\u9FAF]|[\u2605-\u2606]|[\u2190-\u2195]|\u203B)+") | |
russTable = {u'а': u'a',u'б': u'b',u'в': u'v',u'г': u'g',u'д': u'd',u'е': u'e',u'ё': u'e',u'ж': u'zh',u'з': u'z',u'и': u'i',u'й': u'y',u'к': u'k',u'л': u'l',u'м': u'm',u'н': u'n',u'о': u'o',u'п': u'p',u'р': u'r',u'с': u's',u'т': u't',u'у': u'u',u'ф': u'f',u'х': u'h',u'ц': u'ts',u'ч': u'ch',u'ш': u'sh',u'щ': u'sch',u'ъ': u'',u'ы': u'y',u'ь': u'',u'э': u'e',u'ю': u'yu',u'я': u'ya',u'А': u'A',u'Б': u'B',u'В': u'V',u'Г': u'G',u'Д': u'D',u'Е': u'E',u'Ё': u'E',u'Ж': u'Zh',u'З': u'Z',u'И': u'I',u'Й': u'Y',u'К': u'K',u'Л': u'L',u'М': u'M',u'Н': u'N',u'О': u'O',u'П': u'P',u'Р': u'R',u'С': u'S',u'Т': u'T',u'У': u'U',u'Ф': u'F',u'Х': u'H',u'Ц': u'Ts',u'Ч': u'Ch',u'Ш': u'Sh',u'Щ': u'Sch',u'Ъ': u'',u'Ы': u'Y',u'Ь': u'',u'Э': u'E',u'Ю': u'Yu',u'Я': u'Ya'} | |
def checkJap(string): | |
return (kakare.match(string) != None) | |
def japToAscii(string): | |
global russTable | |
try: | |
for letter, transcription in russTable.iteritems(): | |
string = string.replace(letter,transcription) | |
except: | |
pass | |
return kakaconv.do(string) | |
WEATHER_CITY='RSXX0111' | |
didUpdateTable = False | |
global didUpdateTable | |
def getNewBrightnessTable(): | |
global adaptiveBrightnessTable | |
print "Current brightness table:" | |
printTable(adaptiveBrightnessTable) | |
try: | |
result = weather.get_weather_from_weather_com(WEATHER_CITY, 'metric') | |
result = result['forecasts'][0] | |
print "Sunrise: ",result['sunrise'] | |
print "Sunset: ",result['sunset'] | |
adaptiveBrightnessTable = generateTable(result['sunrise'],result['sunset']) | |
print "Got new brightness table:" | |
printTable(adaptiveBrightnessTable) | |
except: | |
print "Error getting new brightness table" | |
adaptiveBrightnessTable = [2, # 12 am | |
2, # 1am | |
2, # 2am | |
2, # 3am | |
2, # 4am | |
2, # 5am | |
2, # 6am | |
3, # 7am | |
4, # 8am | |
4, # 9am | |
5, # 10am | |
6, # 11am | |
7, # 12am | |
7, # 1pm | |
7, # 2pm | |
7, # 3pm | |
7, # 4pm | |
7, # 5pm | |
6, # 6pm | |
6, # 7pm | |
6, # 8pm | |
5, # 9pm | |
4, # 10pm | |
3] # 11pm | |
hrs = 0 | |
global hrs | |
def updateHrs(): | |
now = datetime.datetime.now() | |
hrs = now.strftime("%H") | |
min = now.strftime("%M") | |
global didUpdateTable | |
if int(hrs) == 0 and int(min) == 0 and not didUpdateTable: | |
didUpdateTable = True | |
getNewBrightnessTable() | |
elif int(hrs) == 0 and int(min) > 0 and didUpdateTable: | |
didUpdateTable = False | |
# ============ Connect to MPD as player ============ | |
cli = MPDClient() | |
cli.timeout = 10 | |
cli.idletimeout = None | |
# ============ Open Mixer ============= | |
mix = alsaaudio.Mixer('PCM') | |
mix.setvolume(96) | |
# ============ Open Remote ============= | |
remote = lirc.init('radio', '/radio.lirc') | |
lirc.set_blocking(False, remote) | |
# =========== Open I2c bus ============= | |
bus = smbus.SMBus(1) | |
w.wiringPiSetupGpio() | |
global UP_BTN_PIN, DN_BTN_PIN, PW_BTN_PIN, AV_BTN_PIN, PW_LED_PIN, YE_LED_PIN, GR_LED_PIN, ANT_SELECT_PIN | |
UP_BTN_PIN = 24 | |
DN_BTN_PIN = 23 | |
PW_BTN_PIN = 22 | |
AV_BTN_PIN = 27 | |
ANT_SELECT_PIN = 17 | |
RELAY_SIMULATE_PIN = 7 | |
# ======== Set pin modes ========== | |
# Power Led | |
pwr = LED(PW_PIN) | |
# Yellow Led | |
yel = LED(YE_PIN) | |
# Green Led | |
grn = LED(GR_PIN) | |
w.pinMode(UP_BTN_PIN, 0) | |
w.pinMode(DN_BTN_PIN, 0) | |
w.pinMode(PW_BTN_PIN, 0) | |
w.pinMode(AV_BTN_PIN, 0) | |
w.pinMode(ANT_SELECT_PIN, 1) | |
w.pinMode(RELAY_SIMULATE_PIN, 1) | |
def selectAntenna(toGpio): | |
w.digitalWrite(ANT_SELECT_PIN, not toGpio) | |
def clickRelay(toOn): | |
w.digitalWrite(RELAY_SIMULATE_PIN, not toOn) | |
# Connect antenna to default destination | |
selectAntenna(False) | |
clickRelay(False) | |
# ENABLE PULL UP RESISTORS ON BUTTONS | |
w.pullUpDnControl(UP_BTN_PIN, 2) | |
w.pullUpDnControl(DN_BTN_PIN, 2) | |
w.pullUpDnControl(AV_BTN_PIN, 2) | |
w.pullUpDnControl(PW_BTN_PIN, 2) | |
globalBreaker = False | |
# DISPLAY CONTROLLER INITIALIZATION | |
global addr,cmd,val | |
addr = 0x38 # That's what it was for me at least | |
cmd = 0x00 | |
val = 0x27 | |
bus.write_byte_data(addr, cmd, val) | |
def dispBright(level): # Set brightness to level of 0 to 7 | |
if level > 7: | |
level = 7 | |
if level < 0: | |
level = 0 | |
lvls = ['000','001','010','011','100','101','110','111'] | |
val = int('0'+lvls[level]+'0111',2) | |
bus.write_byte_data(addr,cmd,val) | |
def bright(): # Set brightness to max | |
dispBright(7) | |
def mid(): # Set brightness to medium | |
dispBright(5) | |
def low(): # Lowest brightness, hardly visible in daylight | |
dispBright(3) | |
global point | |
point =0 # This is for the clock dot thing | |
# Handy methods for button reading | |
def pwrBtn(): | |
return not w.digitalRead(22) | |
def modeBtn(): | |
return not w.digitalRead(27) | |
def upBtn(): | |
return not w.digitalRead(24) | |
def downBtn(): | |
return not w.digitalRead(23) | |
# This converts numbers into codes for the SAA chip | |
def numToSeg(num, dotted=0): | |
retv = 0x00 | |
chars = [0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F] | |
if num > 9: | |
num = 9 | |
if num < 0: | |
num = 0 | |
retv = chars[num] | |
if dotted == 1: | |
retv = retv + 0x80 | |
return retv | |
charmap = { '0': numToSeg(0), | |
'1':numToSeg(1), | |
'2':numToSeg(2), | |
'3':numToSeg(3), | |
'4':numToSeg(4), | |
'5':numToSeg(5), | |
'6':numToSeg(6), | |
'7':numToSeg(7), | |
'8':numToSeg(8), | |
'9':numToSeg(9), | |
'':0x00, ' ':0x00, | |
'a':0x77, | |
'b':0x7c, | |
'c':0x39, | |
'd':0x5e, | |
'e':0x79, | |
'f':0x71, | |
'g':0x3d , | |
'h': 0x74, | |
'i':0x04, | |
'j': 0x1E , | |
'k':0x75, | |
'l':0x38, | |
'm':0x54, | |
'n':0x54, | |
'o': 0x5c, | |
'p':0x73, | |
'q':0x67 , | |
'r': 0x50, | |
's': numToSeg(5), | |
't':0x78 , | |
'u':0x1c, | |
'v':0x1c, | |
'w':0x7e, | |
'x':0x76, | |
'y':0x6e, | |
'z':0x1B, | |
'-': 0x40, '_': 0x08, | |
'A':0x77, | |
'B':0x7c, | |
'C':0x39, | |
'D':0x5e, | |
'E':0x79, | |
'F':0x71, | |
'G':0x3d, | |
'H':0x74, | |
'I': numToSeg(1), | |
'J': 0x1E, | |
'K': 0x75, | |
'L': 0x38, | |
'M': 0x37, | |
'N': 0x54, | |
'O': numToSeg(0), | |
'P': 0x73, | |
'Q': 0x67, | |
'R': 0x50, | |
'S': numToSeg(5), | |
'T': 0x78, | |
'U': 0x3E, | |
'V': 0x3E, | |
'W': 0x7e, | |
'X': 0x76, | |
'Y': 0x6e, | |
'Z':0x1B, | |
'(':0x21, '[':0x21, '"':0x22, | |
')':0x0C, ']':0x0C, | |
u'º':0x63, | |
'.':0x80 | |
} | |
# This is the same but also has some characters too | |
def charToSeg(char, dotted=0): | |
retv = 0x00 | |
global charmap | |
chars = charmap | |
try: | |
retv=chars[char] | |
retv = retv + (0x80 * dotted) | |
return retv | |
except: | |
return 0x00 + (0x80 * dotted) | |
curScreen = [0x00,0x00,0x00,0x00] | |
# This writes 4 symbol codes into the SAA bus | |
def writeAll(c1,c2,c3,c4): | |
global curScreen | |
if curScreen[0] != c1: | |
cmd = 0x01 | |
val = c1 | |
bus.write_byte_data(addr,cmd,val) | |
curScreen[0] = c1 | |
if curScreen[1] != c2: | |
cmd = 0x02 | |
val = c2 | |
bus.write_byte_data(addr,cmd,val) | |
curScreen[1] = c2 | |
if curScreen[2] != c3: | |
cmd = 0x03 | |
val = c3 | |
bus.write_byte_data(addr,cmd,val) | |
curScreen[2] = c3 | |
if curScreen[3] != c4: | |
cmd = 0x04 | |
val = c4 | |
bus.write_byte_data(addr,cmd,val) | |
curScreen[3] = c4 | |
updateHrs() | |
def writeFrame(frame): | |
writeAll(frame[0],frame[1],frame[2],frame[3]) | |
def makeStringFrame(msg=' ',d1=0,d2=0,d3=0,d4=0): | |
if len(msg) < 5: | |
msg = msg + ' ' | |
return [charToSeg(msg[0], dotted=d1), charToSeg(msg[1], dotted=d2), charToSeg(msg[2], dotted=d3), charToSeg(msg[3], dotted=d4)] | |
# This writes 4 symbols, d1 d2 d3 and d4 are dots after the respective symbols | |
def writeStr(msg = ' ',d1=0, d2=0,d3=0,d4=0): | |
writeFrame(makeStringFrame(msg,d1,d2,d3,d4)) | |
# Says what it does: make a string with enough lead zeroes to display it on the front panel | |
def intToStrLeadZero(num): | |
if num < 10: | |
return '000'+str(num) | |
elif num < 100: | |
return '00'+str(num) | |
elif num < 1000: | |
return '0'+str(num) | |
else: | |
return str(num) | |
# ============================== MAIN RUN LOOP STARTS HERE ====================================== | |
# Tell the user we are booting | |
dispBright(3) | |
writeStr('load') | |
# Connect to media player | |
cli.connect('localhost', 6600) | |
# Options for reconnect instead of next station | |
cli.single(1) | |
cli.repeat(1) | |
cli.consume(0) | |
getNewBrightnessTable() | |
# FIFO | |
fifo = os.open('/tmp/mpd.fifo', os.O_RDONLY) | |
def fallDigits(digits, reverse=False): | |
global curScreen | |
needTrans = False | |
for digit in range(0,4): | |
if (not (curScreen[digit] == digits[digit])) and not (abs(curScreen[digit] - digits[digit]) == 0x80): | |
needTrans = True | |
break | |
if not needTrans: | |
writeAll(digits[0],digits[1],digits[2],digits[3]) | |
return | |
for frame in (range(0,4) if not reverse else list(reversed(range(0,4)))): | |
for digit in range(0,4): | |
if not (curScreen[digit] == digits[digit]): | |
# transition since it's not equal | |
newOne = 0x0 | |
f = (curScreen[digit] if not reverse else digits[digit]) | |
t = (digits[digit] if not reverse else curScreen[digit]) | |
if (abs(f - t) == 0x80): | |
newOne = t | |
elif frame == 0: | |
newOne = f | |
elif frame == 1: | |
newOne = (4*((f&2)>0) +8*((f&64)>0) + 16*((f&32)>0) + 64*((f&1)>0) + 1 * ((t&8)>0)) | |
elif frame == 2: | |
newOne = 2*((t&4)>0) + 64*((t&8)>0) + 32*((t&16)>0) + 1*((t&64)>0) + 8*((f&1)>0) | |
elif frame == 3: | |
newOne = t | |
writeAll((newOne if digit == 0 else curScreen[0]),(newOne if digit == 1 else curScreen[1]),(newOne if digit == 2 else curScreen[2]),(newOne if digit == 3 else curScreen[3])) | |
time.sleep(0.1) | |
# This is the clock 'thread' (it's all linear in fact lol) | |
def clock(): | |
global point | |
global hrs | |
if point == 0: | |
point = 1 | |
else: | |
point = 0 | |
point = point or isFMCast # Low EMI mode | |
now = datetime.datetime.now() | |
hrs = now.strftime("%H") | |
min = now.strftime("%M") | |
# Would be nice to dim it at night, have it at mid in the morning and bright in the daytime | |
dispBright(adaptiveBrightnessTable[int(hrs)]) | |
h_0 = int(hrs[0]) | |
h_1 = int(hrs[1]) | |
m_0 = int(min[0]) | |
m_1 = int(min[1]) | |
# Output 4 numbers | |
fallDigits([numToSeg(h_0), numToSeg(h_1, dotted=point), numToSeg(m_0), numToSeg(m_1)]) | |
def transMessage(message,d1=0,d2=0,d3=0,d4=0): | |
global hrs | |
for i in range(0,7): | |
dispBright(7-i) | |
w.delay(50) | |
writeStr(message,d1,d2,d3,d4) | |
for i in range(0,7): | |
dispBright(i) | |
w.delay(50) | |
def transClock(): | |
now = datetime.datetime.now() | |
hrs = now.strftime("%H") | |
mins = now.strftime("%M") | |
buildIn(hrs+mins) | |
# Vars for station and total number of them | |
global station, stationMax | |
station = int(cli.status()['song']) | |
stationMax = int(cli.status()['playlistlength'])-1 | |
# Recording stuff | |
global isRecing, recStation, recLedSt, recorder | |
isRecing = 0 | |
recStation = 0 | |
recName = '' | |
recLedSt = 0 | |
recorder = None | |
# Sleep timer stuff | |
global sleepEnabled, sleepBegan, sleepDur, powerIsOn | |
sleepEnabled = 0 | |
sleepBegan = 0 | |
sleepDur = 0 | |
powerIsOn = 0 | |
# Menu and clock mode stuff | |
global menu, clockOnPlayer | |
clockOnPlayer = 0 | |
# ================== Record button or menu item function | |
def toggleRec(): | |
global isRecing, recStation, recLedSt, recorder, recName | |
if isRecing == 0: # We aren't recording | |
recLedSt = 1 # Rec led state | |
recStation = station # Rec station Number | |
isRecing = 1 # is recording | |
cs = cli.currentsong() | |
if 'name' in cs: | |
recName = cs['name'] | |
surl = cs['file'] # Get the stream URL | |
recorder = subprocess.Popen((['streamripper',surl,'-d','/recordings', '-u','vlc 1.1.0-git-20100330-0003','--quiet'])) # Start streamripper into /recordings | |
snake(1) | |
scrollMessage('recording strt', 0.1, holdOff=0.5, moveIn=True, makeBuildIn=False, fadeIn=True) # Tell the user that we start now | |
grn.setValue(100) | |
time.sleep(0.25) | |
else: # We are recording now | |
recorder.terminate() # Kill streamripper | |
snake(1) | |
scrollMessage('record stop', 0.1, holdOff=0.5, moveIn=True, makeBuildIn=False, fadeIn=True) # Tell the user that we start now | |
grn.setValue(0) | |
recLedSt = 0 # Reset some vars | |
recStation = 0 | |
isRecing = 0 | |
recName = '' | |
# ===================== Info menu item method | |
def about(): | |
bright() # Full brightness | |
time.sleep(0.5) | |
splashAnim(step = 0.05) | |
writeStr('gen ') | |
time.sleep(0.3) | |
writeStr(' ji ') | |
time.sleep(0.3) | |
writeStr(' tsu') | |
time.sleep(0.9) | |
scrollMessage('gadget lab', 0.1, holdOff=0.2) | |
writeStr('0 ') | |
time.sleep(0.3) | |
writeStr(' 0 ') | |
time.sleep(0.3) | |
writeStr(' 1 ') | |
time.sleep(0.9) | |
writeStr(' ') | |
time.sleep(0.5) | |
# ==================== Next button method | |
def nextSt(): | |
global cli, station, stationMax | |
try: | |
cli.next() | |
except: # This likes to crash sometimes so... | |
try: | |
cli.disconnect() | |
cli.connect('localhost', 6600) | |
cli.next() | |
except: # And when even that crashes... | |
writeStr('shit') | |
pwr.setValue(100) | |
yel.setValue(100) | |
grn.setValue(100) | |
time.sleep(1) | |
# Update display | |
station = int(cli.status()['song']) | |
stationMax = int(cli.status()['playlistlength'])-1 | |
ststr = intToStrLeadZero(station+1) | |
if clockOnPlayer or isInVis: | |
writeStr(ststr) | |
else: | |
fallDigits([charToSeg(ststr[0]),charToSeg(ststr[1]),charToSeg(ststr[2]),charToSeg(ststr[3])]) | |
if isRecing == 1: # Is recording | |
if recStation != station and grn.state != ST_BREATHE: # Recording NOT the station we're listening to | |
grn.startBreathing() | |
else: # Recording exactly what we're listening to | |
grn.setValue(100) | |
# ==================== Prev button method, see above | |
def prevSt(): | |
global cli, station, stationMax | |
try: | |
cli.previous() | |
except: | |
try: | |
cli.disconnect() | |
cli.connect('localhost', 6600) | |
cli.previous() | |
except: | |
writeStr('shit') | |
pwr.setValue(100) | |
yel.setValue(100) | |
grn.setValue(100) | |
time.sleep(1) | |
station = int(cli.status()['song']) | |
stationMax = int(cli.status()['playlistlength'])-1 | |
ststr = intToStrLeadZero(station+1) | |
if clockOnPlayer or isInVis: | |
writeStr(ststr) | |
else: | |
fallDigits([charToSeg(ststr[0]),charToSeg(ststr[1]),charToSeg(ststr[2]),charToSeg(ststr[3])], reverse=True) | |
if isRecing == 1: # Is recording | |
if recStation != station and grn.state != ST_BREATHE: # Recording NOT the station we're listening to | |
grn.startBreathing() | |
else: # Recording exactly what we're listening to | |
grn.setValue(100) | |
# =============== Weather disp | |
def showWeather(): | |
#try: | |
result = weather.get_weather_from_weather_com(WEATHER_CITY, 'metric') | |
result = result['current_conditions'] | |
rstr = str(result['temperature']) + u'ºC, '+result['text'] | |
rstr = rstr.encode('utf-8') | |
scrollMessage(rstr, moveIn=False, makeBuildIn=True, holdOn=1) | |
#except: | |
# pass | |
# =============== Handy checking for two things that are used to power the thing on | |
def powerOnCondition(): | |
pbtn = pwrBtn() # Get pwr button | |
rbtn = False | |
lcode = lirc.nextcode() # Get IR code | |
if len(lcode) > 0: | |
if lcode[0] == u'pwron': # If IR button is pressed | |
rbtn = True | |
else: | |
lcode = [' '] | |
if modeBtn() or lcode[0] == 'time': | |
dbgMenu() | |
elif lcode[0] == 'help' or upBtn(): | |
showWeather() | |
elif downBtn() or lcode[0] == 'red': | |
fmRadio() | |
return pbtn or rbtn # Return this or that | |
# ============= Hacklet to get current IR code | |
def irCode(): | |
lcode = lirc.nextcode() # Get code | |
if len(lcode) > 0: # We have one | |
pwr.blinkOnce() | |
return lcode[0] # Return code | |
return '' # No button was pressed if we end up here | |
# ============ Sleep mode menu thread | |
def sleepMenu(): | |
global YE_LED_PIN, sleepEnabled, sleepBegan, sleepDur | |
modes = [0,1,5,30,60,90,120] # Sleep mode times presets | |
smode = 0 # Selected mode | |
buildIn('set ') | |
time.sleep(1) | |
ic = '' # IR codes will be read here | |
while not (modeBtn() or ic == 'ok'): # While not OK button or mode button | |
ic = irCode() # Read IR code | |
if smode == 0: # 0 minutes is in fact 'None' | |
writeStr('none') | |
else: | |
writeStr(str(modes[smode]) + 'm') | |
if upBtn() or ic == 'up': # Up button handling | |
smode = smode +1 | |
if smode >= len(modes): # Menu is cycled | |
smode = 0 | |
if downBtn() or ic == 'down': # Down button handling | |
smode = smode -1 | |
if smode < 0: | |
smode = len(modes)-1 | |
time.sleep(0.25) # Give the CPU a rest | |
if smode == 0: # User selected None | |
if sleepEnabled == 1: # if sleep mode was on, however | |
yel.setValue(0) | |
sleepEnabled = 0 # And reset the shit | |
sleepBegan = 0 | |
sleepDur = 0 | |
else: # User chose some mode | |
if sleepEnabled == 0: # if sleep mode was off, however | |
yel.setValue(100) | |
sleepEnabled = 1 # Set up some variables | |
sleepBegan = datetime.datetime.now() | |
sleepDur = modes[smode] | |
menu = ['disp', 'cloc', 'canc', 'info', 'slep', 'rec ', 'fm r', 'tran', 'nowp'] # This is top menu entries | |
# Display Clock Cancel About Sleep Record FM Radio FM Now | |
# Station# Display Menu This Mode Start/stop Transmit Playing | |
def topMenu(): | |
global menu, clockOnPlayer | |
menuItem = 0 # cur sel item | |
transMessage('pref') | |
time.sleep(1) | |
a = irCode() | |
while not (modeBtn() or a == 'ok'): | |
a = irCode() # Read IR code | |
if a == 'menu': # Menu button on remote exits menu | |
menuItem = -1 | |
break | |
writeStr(menu[menuItem]) # Display current item | |
if (downBtn() or a == 'down'): # Down button | |
menuItem = menuItem +1 | |
if menuItem >= len(menu): # Menu is cycled | |
menuItem = 0 | |
if (upBtn() or a == 'up'): # Up button | |
menuItem = menuItem -1 | |
if menuItem < 0: | |
menuItem = len(menu)-1 | |
time.sleep(0.2) | |
if menuItem == 0: # Display Station No. | |
clockOnPlayer = 0 | |
elif menuItem == 1: # Display Clock While On | |
clockOnPlayer = 1 | |
elif menuItem == 3: # About this software | |
about() | |
elif menuItem == 4: | |
sleepMenu() | |
elif menuItem == 5: | |
# record | |
toggleRec() | |
elif menuItem == 6: | |
fmRadio() | |
elif menuItem == 7: | |
fmCastOn() | |
elif menuItem == 8: | |
nowPlaying() | |
dbgmenu = ['reld', 'rebo', 'canc'] | |
def dbgMenu(): | |
global dbgmenu, clockOnPlayer | |
menuItem = 0 # cur sel item | |
transMessage('serv') | |
time.sleep(1) | |
a = irCode() | |
while not (modeBtn() or a == 'ok'): | |
a = irCode() # Read IR code | |
if a == 'menu': # Menu button on remote exits menu | |
menuItem = -1 | |
break | |
writeStr(dbgmenu[menuItem]) # Display current item | |
if (downBtn() or a == 'down'): # Down button | |
menuItem = menuItem +1 | |
if menuItem >= len(dbgmenu): # Menu is cycled | |
menuItem = 0 | |
if (upBtn() or a == 'up'): # Up button | |
menuItem = menuItem -1 | |
if menuItem < 0: | |
menuItem = len(dbgmenu)-1 | |
time.sleep(0.2) | |
if menuItem == 0: # Display Station No. | |
pwr.setValue(0) | |
transMessage('rest') | |
try: | |
cli.stop() | |
cli.disconnect() | |
except: | |
pass | |
os.execl('/radio.py', '/usr/bin/python /radio.py') | |
os.exit() | |
elif menuItem == 1: # Display Clock While On | |
pwr.setValue(50) | |
transMessage('boot') | |
try: | |
cli.stop() | |
cli.disconnect() | |
except: | |
pass | |
os.execl('/sbin/reboot','/sbin/reboot') | |
os.exit() | |
@atexit.register | |
def onExit(): # If the system is going for a reboot this will be useful | |
pwr.setValue(0) | |
yel.setValue(0) | |
grn.setValue(0) | |
selectAntenna(False) | |
clickRelay(False) | |
if isRecing == 1: # Stop recorder if needed | |
toggleRec() | |
transMessage('boot') # Show 'boot' on display | |
# for better responsiveness in animations | |
def showAnimation(animation, loops=1, delay=0.1): | |
global globalBreaker, powerIsOn, mustEnterAirport | |
timerCounter = 0 | |
for i in range(0,loops): | |
for frame in animation: | |
writeFrame(frame) | |
timerCounter = 0 | |
while timerCounter < delay: | |
if powerIsOn == 1 and not mustEnterAirport: | |
irThread() | |
if globalBreaker: | |
return | |
timerCounter += 0.01 | |
time.sleep(0.01) | |
def splashAnim(count=5, step = 0.3): | |
count = count & 0x0F | |
showAnimation([ | |
[0x00,0x40,0x40,0x00], | |
[0x39,0x09,0x09,0x0F] | |
], count, step) | |
writeAll(0x00,0x00,0x00,0x00) | |
def snake(count=5, step = 0.1): | |
count = count & 0x0F | |
showAnimation([ | |
[0x00,0x00,0x00,0x01], | |
[0x00,0x00,0x01,0x01], | |
[0x00,0x01,0x01,0x01], | |
[0x01,0x01,0x01,0x00], | |
[0x21,0x01,0x00,0x00], | |
[0x31,0x00,0x00,0x00], | |
[0x38,0x00,0x00,0x00], | |
[0x18,0x08,0x00,0x00], | |
[0x08,0x08,0x08,0x00], | |
[0x00,0x08,0x08,0x08], | |
[0x00,0x00,0x08,0x0C], | |
[0x00,0x00,0x00,0x0E], | |
[0x00,0x00,0x00,0x06], | |
[0x00,0x00,0x00,0x02], | |
[0x00,0x00,0x00,0x00] | |
], count, step) | |
def showPolyTransition(sourceMasks, destinationMasks, destinationScreen, simultaneous=True, delay=0.06): | |
frames = [] | |
frame = [] | |
sourceScreen = curScreen | |
if len(sourceMasks) != len(destinationMasks): | |
print "ShowPolyTransition: Malformed Transition Data, masks arrays are not of same length!" | |
return | |
if simultaneous: | |
# Generate all characters at the same time | |
for i in range(0, len(sourceMasks)): | |
frame = [] | |
for j in range(0,4): | |
frame.append((sourceScreen[j] & sourceMasks[i]) | (destinationScreen[j] & destinationMasks[i])) | |
frames.append(frame) | |
else: | |
for j in range(0,4): | |
for i in range(0, len(sourceMasks)): | |
frame = copy.copy(sourceScreen) | |
frame[j] = ((sourceScreen[j] & sourceMasks[i]) | (destinationScreen[j] & destinationMasks[i])) | |
frames.append(frame) | |
sourceScreen = frame | |
showAnimation(frames, delay=delay) | |
def showTransition(masks, destinationScreen, simultaneous=True,delay=0.06): | |
destMasks = [] | |
for mask in masks: | |
destMasks.append(~mask) | |
showPolyTransition(masks, destMasks, destinationScreen, simultaneous, delay) | |
def buildIn(message, barred=False, keepCurrent=True, simultaneous=True, delay=0.06): | |
masks = [] | |
message += ' ' | |
if barred: | |
masks = [0xFF, 0xCF, 0x86, 0x80, 0x0] | |
else: | |
masks = [0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x0] | |
showTransition(masks, makeStringFrame(message), simultaneous, delay) | |
def buildFill(message, direction=0, barred=False, simultaneous=True, delay=0.06): | |
buildIn('8888',direction,barred,True,simultaneous,delay) | |
buildIn(message,direction,barred,True,simultaneous,delay) | |
def scrollMessage(message, delay=0.3, reverse=False, holdOff=1, moveIn=False, fadeIn=False, makeBuildIn=True, holdOn=0, moveOut=False): | |
#if checkJap(message.decode('utf-8')): | |
message = japToAscii(message.decode('utf-8')) | |
#if checkJap(message): | |
message = japToAscii(message) | |
if moveOut: | |
message = message + ' ' | |
if moveIn: | |
if fadeIn: | |
transMessage(' ') | |
mtemp = ' '+message[0]+message[1]+message[2]+ message[3] | |
for i in range(0,len(mtemp)-3): | |
writeStr(mtemp[i]+mtemp[i+1]+mtemp[i+2]+ mtemp[i+3]) | |
irThread() | |
if globalBreaker: | |
return | |
time.sleep(delay) | |
elif fadeIn and makeBuildIn: | |
transMessage(' ') | |
buildIn(message) | |
elif fadeIn: | |
transMessage(message[0]+message[1]+message[2]+ message[3]) | |
elif makeBuildIn: | |
buildIn(message) | |
time.sleep(holdOn) | |
for i in range(0,len(message)-3): | |
writeStr(message[i]+message[i+1]+message[i+2]+ message[i+3]) | |
irThread() | |
if globalBreaker: | |
return | |
time.sleep(delay) | |
if reverse: | |
time.sleep(holdOff) | |
for i in reversed(range(0,len(message)-3)): | |
writeStr(message[i]+message[i+1]+message[i+2]+ message[i+3]) | |
irThread() | |
if globalBreaker: | |
return | |
time.sleep(delay) | |
irThread() | |
if globalBreaker: | |
return | |
time.sleep(holdOff) | |
# MPC viso | |
visFillBar = ['00001000', '01001000', '01001001'] | |
def visFillBarW(v): | |
v = int(v) | |
if v < 0: | |
v = 0 | |
if v >= len(visFillBar): | |
v = len(visFillBar)-1 | |
return int(visFillBar[v],2) | |
def visFrame(): | |
try: | |
rawStream = os.read(fifo, 4096) | |
except OSError as err: | |
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK: | |
rawStream = None | |
else: | |
pass | |
if rawStream: | |
leftChannel = audioop.tomono(rawStream, 2, 1, 0) | |
rightChannel = audioop.tomono(rawStream, 2, 0, 1) | |
stereoPeak = audioop.max(rawStream, 2) | |
leftPeak = audioop.max(leftChannel, 2) | |
rightPeak = audioop.max(rightChannel, 2) | |
leftDB = 20 * math.log10(leftPeak) -74 | |
rightDB = 20 * math.log10(rightPeak) -74 | |
# print(rightPeak, leftPeak, rightDB, leftDB) | |
valR = math.floor( (rightDB) /20 *4) -1 | |
valL = math.floor( (leftDB) /20 *4) -1 | |
valC1 = 0.5*valR+(valL*valL) | |
valC2 = (valR*valR)+0.5*valL | |
# print (valL,valC1,valC2,valR) | |
valR = min(valR, len(visFillBar)) | |
valL = min(valL, len(visFillBar)) | |
valC1 = min(valC1, len(visFillBar)) | |
valC2 = min(valC2, len(visFillBar)) | |
writeAll( visFillBarW(valL)+0x80, visFillBarW(valC1),visFillBarW(valC2)+0x80, visFillBarW(valR) ) | |
def nowPlaying(): | |
csong = cli.currentsong() | |
global isRecing, recStation, station | |
if isRecing and recStation != station: | |
snake(1,0.05) | |
snum = str(recStation) | |
if not (recName == ''): | |
snum = snum + ' - '+recName | |
scrollMessage('recording station '+snum, 0.1, True, 0.5, False,True) | |
if not 'title' in csong: | |
return | |
scrollMessage(csong['title'], 0.2, True, 1) | |
scrollMessage(csong['name'], 0.2, True, 1) | |
buildIn(intToStrLeadZero(station+1)) | |
global lastDispChangeDate, lastDispTag | |
lastDispChangeDate = datetime.datetime.now() | |
lastDispTag = csong['title'] | |
fmFreq = 104.0 | |
fmLower = 30.0 | |
fmUpper = 2000.0 | |
radioBreaker = False | |
fmCaster = None | |
isFMCast = False | |
rdsFifo = None | |
fmCastUpper = 110 | |
fmCastLower = 80 | |
fmCastFreq = 110 | |
def fmCastOn(): | |
if isFMCast: | |
return | |
global fmCaster, isFMCast, rdsFifo, fmCastFreq, fmCastLower, fmCastUpper | |
scrollMessage("input frequency then press ok", holdOn=0.5, delay=0.1) | |
code = '' | |
num = str(fmCastFreq) | |
digits = 0 | |
dot = 3 | |
while ((not code == 'ok')) : | |
writeStr(num,d1 = (1 == dot),d2 = (2 == dot), d3 = (3 == dot), d4=(4 == dot)) | |
code = irCode() | |
if code.isdigit(): | |
num = num + code | |
digits = digits + 1 | |
elif code == 'left': | |
if dot > 2: | |
dot = dot - 1 | |
elif code == 'right': | |
if dot < 3: | |
dot = dot + 1 | |
elif code == 'exit': | |
num = '' | |
digits = 0 | |
elif code == 'menu': | |
return | |
time.sleep(0.3) | |
fstr = '' | |
num = num + '0000' | |
if 2 == dot: | |
fstr = num[0] + num[1]+ '.'+num[2]+num[3] | |
elif 3 == dot: | |
fstr = num[0] + num[1]+ num[2]+'.'+num[3] | |
elif 4 == dot: | |
fstr = num[0] + num[1]+ num[2]+num[3] | |
fr = float(fstr) | |
if fr >= fmCastLower and fr <= fmCastUpper: | |
fmCastFreq = fr | |
else: | |
return | |
selectAntenna(True) | |
time.sleep(0.5) | |
fmCaster = subprocess.Popen('/fmcast 49500 '+str(fmCastFreq), stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | |
isFMCast = True | |
scrollMessage("tune to fm "+str(fmCastFreq), delay=0.2, holdOff=0.2) | |
pwr.setValue(30) | |
#rdsFifo = open('/tmp/rds.fifo', 'w') | |
time.sleep(1) | |
def fmCastOff(): | |
if not isFMCast: | |
return | |
scrollMessage('reboot to off transmitter', delay=0.1) | |
os.system("reboot") | |
def fmRadio(): | |
if isFMCast: | |
transMessage("cant", d3=1) | |
time.sleep(1) | |
return | |
since = -1 | |
global fmFreq, fmLower, fmUpper, radioBreaker | |
wasConn = 1 | |
try: | |
cli.stop() | |
cli.disconnect() | |
except: | |
pwr.setValue(50) | |
wasConn = 0 | |
snake(2,0.1) | |
mix.setvolume(100) | |
scrollMessage('fm radio ', holdOff=0, delay=0.07) | |
sas = str(fmFreq) | |
pro = subprocess.Popen('/fmradio '+str(fmFreq)+'M', stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | |
transMessage(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
code = irCode() | |
favs = [104.0, | |
103.5, 104.0, 106.0, | |
88.1, 68.25, 104.0] | |
while ((not code == 'pwron') and (not pwrBtn()) ) and ((not code == 'menu') and (not modeBtn()) ): | |
# Run until power must be off or user exited | |
code = irCode() | |
if not ((not code == 'pwron') and (not pwrBtn()) ) or ((not code == 'menu') and (not modeBtn()) ): | |
if code == 'up' or code == 'next' or upBtn(): | |
fmFreq = fmFreq + 0.5 | |
if fmFreq > fmUpper: | |
fmFreq = fmLower | |
sas = str(fmFreq) | |
since = 0 | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
yel.setValue(50) | |
grn.setValue(0) | |
elif code == 'left': | |
fmFreq = fmFreq - 0.1 | |
if fmFreq < fmLower: | |
fmFreq = fmUpper | |
sas = str(fmFreq) | |
since = 0 | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
yel.setValue(50) | |
grn.setValue(0) | |
elif code == 'right': | |
fmFreq = fmFreq + 0.1 | |
if fmFreq > fmUpper: | |
fmFreq = fmLower | |
sas = str(fmFreq) | |
since = 0 | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
yel.setValue(50) | |
grn.setValue(0) | |
elif code == 'down' or code == 'prev' or downBtn(): | |
fmFreq = fmFreq - 0.5 | |
if fmFreq < fmLower: | |
fmFreq = fmUpper | |
sas = str(fmFreq) | |
since = 0 | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
yel.setValue(50) | |
grn.setValue(0) | |
elif code == 'ok' and since > -1: | |
since = 6 | |
elif code.isdigit(): # Digit buttons | |
num = code | |
digits = 0 | |
dot = 2 | |
yel.setValue(50) | |
grn.setValue(0) | |
while (digits < 3) and (not code == 'ok'): | |
writeStr(num,d1 = (1 == dot),d2 = (2 == dot), d3 = (3 == dot), d4=(4 == dot)) | |
code = irCode() | |
if code.isdigit(): | |
num = num + code | |
digits = digits + 1 | |
elif code == 'left': | |
if dot > 2: | |
dot = dot - 1 | |
elif code == 'right': | |
if dot < 4: | |
dot = dot + 1 | |
time.sleep(0.3) | |
yel.setValue(0, smooth=False) | |
grn.setValue(100, smooth=False) | |
fstr = '' | |
num = num + '0000' | |
if 2 == dot: | |
fstr = num[0] + num[1]+ '.'+num[2]+num[3] | |
elif 3 == dot: | |
fstr = num[0] + num[1]+ num[2]+'.'+num[3] | |
elif 4 == dot: | |
fstr = num[0] + num[1]+ num[2]+num[3] | |
fr = float(fstr) | |
if fr >= fmLower and fr <= fmUpper: | |
fmFreq = fr | |
os.killpg(pro.pid, signal.SIGTERM) | |
pro = subprocess.Popen('/fmradio '+str(fmFreq)+'M', stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | |
sas = str(fmFreq) | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
grn.setValue(0, smooth=False) | |
elif code == 'yellow': | |
splashAnim(5, 0.1) | |
transMessage('scan') | |
grn.setValue(50, smooth=False) | |
code = irCode() | |
tmr = 0 | |
while not (code == 'yellow' or code == 'menu' or code == 'ok'): | |
code = irCode() | |
tmr = tmr + 1 | |
if tmr >= 6: | |
fmFreq = fmFreq + 0.5 | |
if fmFreq > fmUpper: | |
fmFreq = fmLower | |
sas = str(fmFreq) | |
writeStr(sas.replace('.',''),d1=0,d2=(sas[2] == '.'),d3=(sas[3] == '.')) | |
os.killpg(pro.pid, signal.SIGTERM) | |
pro = subprocess.Popen('/fmradio '+str(fmFreq)+'M', stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | |
tmr = 0 | |
time.sleep(0.5) | |
grn.setValue(0,smooth=False) | |
time.sleep(0.5) | |
if since > -1: | |
since = since +1 | |
if since >= 6: | |
since = -1 | |
yel.setValue(1) | |
os.killpg(pro.pid, signal.SIGTERM) | |
pro = subprocess.Popen('/fmradio '+str(fmFreq)+'M', stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid) | |
time.sleep(1) | |
yel.setValue(0) | |
grn.setValue(0) | |
radioBreaker = True | |
os.killpg(pro.pid, signal.SIGTERM) | |
mix.setmute(0) | |
#mix.setvolume(88) | |
if wasConn == 1: | |
cli.connect('localhost', 6600) # Set up media player | |
time.sleep(1) | |
cli.single(1) | |
cli.repeat(1) | |
cli.consume(0) | |
def airportMode(): | |
clickRelay(True) | |
global mustEnterAirport | |
global airportInfoChanged | |
global airportInfoUpdate | |
global airportInfoLine | |
#print "Airport Mode" | |
scrollMessage("airport mode", delay=0.1, reverse=True, moveIn=True, fadeIn=True, makeBuildIn=False) | |
while mustEnterAirport: | |
if airportInfoChanged: | |
delta = datetime.datetime.now() - airportInfoUpdate | |
if delta.seconds >= 60*10 or delta.seconds > 3: | |
#print "NEW META DISP" | |
#print "METALEN", len(airportInfoLine) | |
airportInfoChanged = False | |
try: | |
snake(1) | |
if len(airportInfoLine) > 0: | |
scrollMessage(airportInfoLine, 0.1, True, 1, moveIn=True, fadeIn=False) | |
splashAnim(2) | |
except: | |
pass | |
time.sleep(0.5) | |
if pwrBtn() and modeBtn(): | |
mustEnterAirport = False | |
writeStr('airp') | |
clickRelay(False) | |
writeStr(' ') | |
time.sleep(1) | |
# ======================= This is the IR scanner that runs in a While loop | |
def irThread(): | |
global globalBreaker | |
global mustEnterAirport | |
lcode = lirc.nextcode() # Get code | |
if len(lcode) > 0: # We have a code | |
global lastDispChangeDate | |
lastDispChangeDate = datetime.datetime.now() | |
globalBreaker = True | |
if lcode[0] != 'pwron': | |
pwr.blinkOnce() | |
if lcode[0] == 'time': # Clock button, same as clock in menu | |
global clockOnPlayer | |
clockOnPlayer = not clockOnPlayer | |
if clockOnPlayer: | |
transClock() | |
else: | |
transMessage(intToStrLeadZero(station+1), d1 = (station == 0), d4 = (station == stationMax)) | |
elif lcode[0] == 'red': # Red record button | |
toggleRec() | |
elif lcode[0] == 'menu': # Menu button opens top menu | |
topMenu() | |
elif lcode[0] == 'up' or lcode[0] == 'next': # Up/P+ buttons -- +1 station | |
nextSt() | |
elif lcode[0] == 'down' or lcode[0] == 'prev': # Down/P- buttons -- -1 station | |
prevSt() | |
elif lcode[0] == 'yellow': # Yellow button -- sleep menu | |
sleepMenu() | |
elif lcode[0] == 'green': | |
nowPlaying() | |
elif lcode[0] == 'blue': | |
fmRadio() | |
elif lcode[0] == 'rcl': | |
global isInVis | |
isInVis = not isInVis | |
elif lcode[0] == 'volup': | |
v = mix.getvolume()[0] | |
if v == 100L: | |
return | |
mix.setvolume(v+1) | |
v = mix.getvolume()[0] | |
v = str(v) | |
writeAll( | |
charToSeg(v[0]), | |
charToSeg(v[1]) if int(v) > 9 else 0x00, | |
charToSeg(v[2]) if int(v) > 99 else 0x00, | |
visFillBarW(math.floor(int(v)/50)) | |
) | |
time.sleep(0.25) | |
elif lcode[0] == 'voldn': | |
v = mix.getvolume()[0] | |
if v == 0L: | |
return | |
mix.setvolume(v-1) | |
v = mix.getvolume()[0] | |
v = str(v) | |
writeAll( | |
charToSeg(v[0]), | |
charToSeg(v[1]) if int(v) > 9 else 0x00, | |
charToSeg(v[2]) if int(v) > 99 else 0x00, | |
visFillBarW(math.floor(int(v)/70)) | |
) | |
time.sleep(0.25) | |
elif lcode[0] == 'help': | |
showWeather() # fuck all this romantic stuff, let's have functionality instead | |
# BEFORE: ? button shows an important counter value | |
#sd = datetime.datetime(2014, 9, 3, 0, 0, 0) | |
#now = datetime.datetime.now() | |
#delta = now - sd | |
#days = str(delta.days)+'d' | |
#transMessage(days) | |
#time.sleep(2) | |
#if clockOnPlayer: | |
# transClock() | |
#else: | |
# transMessage(intToStrLeadZero(station+1), d1 = (station == 0), d4 = (station == stationMax)) | |
elif lcode[0].isdigit(): # Digit buttons | |
digits = 0 | |
num = lcode[0] | |
a = '' | |
transMessage(num) | |
# Enter number input mode | |
while (a != 'ok' and a != 'menu' and digits != 3): # While we didn't press OK to confirm, Menu to cancel or didn't enter 4 digits | |
writeStr(num) # Display input | |
a = irCode() # Get next IR code | |
if a.isdigit(): # If code is digit | |
num = num + a # Write it in | |
digits = digits + 1 | |
w.delay(5) | |
global station, cli, stationMax | |
if int(num) <= stationMax+1 and int(num) > 0 and a != 'menu': # Input number is valid, and we didn't press Menu to cancel | |
# Update the station no. and play it | |
station = int(num)-1 | |
cli.play(station) | |
writeStr(intToStrLeadZero(station+1)) | |
elif lcode[0] == u'pwron': # Power button on remote, this is needed | |
return True | |
else: | |
globalBreaker = (pwrBtn() or upBtn() or downBtn() or modeBtn()) | |
if globalBreaker: | |
# debounce buttons | |
time.sleep(0.1) | |
return pwrBtn() or mustEnterAirport # this is needed too | |
writeStr("0000") | |
lastDispChangeDate = datetime.datetime.now() | |
lastDispTag = '' | |
globalBreaker = False | |
isInVis = False | |
about() | |
#Enable Airport | |
def APThreadCore(): | |
processor = Processor() | |
processor.add_listener(AirportGoes) # function `event_processor` defined bellow. | |
processor.add_listener(AirportEvent) # function `event_processor` defined bellow. | |
processor.parse() # this will probably* run forever. (* If it doesn't crash, lol) | |
APThread = threading.Thread(group=None, target=APThreadCore) | |
APThread.daemon = True | |
APThread.start() | |
# ==================================== Now that's where it's at ================================ | |
while True: | |
# try: | |
# We've just powered off or just booted | |
# Reset variables | |
now = datetime.datetime.now() | |
hrs = now.strftime("%H") | |
sleepEnabled = 0 | |
sleepDur = 0 | |
sleepBegan = 0 | |
powerIsOn = 0 | |
# Shut down audio | |
cli.stop() | |
cli.disconnect() | |
fmCastOff() | |
yel.setValue(0) | |
# Pretty fade the power LED | |
pwr.setValue(100) | |
time.sleep(1) | |
transClock() | |
if isRecing == 1: | |
grn.startBreathing() | |
else: | |
grn.setValue(0) | |
# While powered off | |
while not (powerOnCondition()): | |
clock() # Show clock | |
if mustEnterAirport: | |
airportMode() | |
if powerOnCondition(): | |
break | |
time.sleep(0.5) | |
clickRelay(True) | |
now = datetime.datetime.now() | |
hrs = now.strftime("%H") | |
if int(hrs) < 12 and int(hrs) >= 6: | |
scrollMessage('good morning',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
elif int(hrs) >= 12 and int(hrs) < 18: | |
scrollMessage('hello',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
elif int(hrs) >= 18 and int(hrs) < 21: | |
scrollMessage('good evening ',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
else: | |
scrollMessage('hello ',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
cli.connect('localhost', 6600) # Set up media player | |
cli.single(1) | |
cli.repeat(1) | |
cli.consume(0) | |
# Pretty fade out | |
pwr.setValue(0) | |
grn.setValue(0) | |
# Update recording LED | |
if isRecing == 1: # Is recording | |
if recStation != station and grn.state != ST_BREATHE: # Recording NOT the station we're listening to | |
grn.startBreathing() | |
else: # Recording exactly what we're listening to | |
grn.setValue(100) | |
# Initialize audio | |
mix.setmute(0) | |
cli.play() | |
powerIsOn = 1 | |
if clockOnPlayer: | |
transClock() | |
else: | |
transMessage(intToStrLeadZero(station+1), d1 = (station == 0), d4 = (station == stationMax)) | |
# While irThread returns False (so no power button was hit) | |
lastDispChangeDate = datetime.datetime.now() | |
while not ( irThread() or radioBreaker ): | |
if not clockOnPlayer == 1: # Display mode is Station | |
delta = datetime.datetime.now() - lastDispChangeDate | |
csong = cli.currentsong() | |
try: | |
if 'title' in csong: | |
if (delta.seconds >= 60*10 or lastDispTag != csong['title']) and delta.seconds > 3: | |
snake(1) | |
lastDispTag = csong['title'] | |
#if isFMCast: | |
# rdsFifo.write("RT "+str(lastDispTag)) | |
if len(csong['title']) > 0: | |
scrollMessage(csong['title'], 0.1, True, 1, moveIn=True, fadeIn=False) | |
if not globalBreaker: | |
splashAnim(2) | |
lastDispChangeDate = datetime.datetime.now() | |
except: | |
pass | |
if not isInVis: | |
writeStr(intToStrLeadZero(station+1), d1 = (station == 0), d4 = (station == stationMax)) | |
else: | |
visFrame() | |
globalBreaker = False | |
else: # Display mode is Clock while on | |
clock() | |
lastDispChangeDate = datetime.datetime.now() | |
if sleepEnabled == 1: # Sleep mode is on | |
sleepLen = (datetime.datetime.now() - sleepBegan).seconds / 60 # Check for how long it was | |
if sleepLen >= sleepDur: # If it's time to sleep, | |
break # skip to power off | |
if upBtn(): | |
nextSt() | |
if downBtn(): | |
prevSt() | |
if modeBtn(): | |
topMenu() | |
time.sleep(0.1 if (not isInVis or clockOnPlayer == 1) else 0.01) | |
radioBreaker = False | |
if mustEnterAirport: | |
cli.stop() | |
airportMode() | |
if int(hrs) < 12 and int(hrs) >= 6: | |
scrollMessage('see you',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
elif int(hrs) >= 12 and int(hrs) < 18: | |
scrollMessage('bye-bye',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
elif int(hrs) >= 18 or int(hrs) < 6: | |
scrollMessage('good night',delay=0.1, holdOff = 0, holdOn = 0, moveIn=True, fadeIn=True, moveOut = True) | |
clickRelay(False) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment