Exemple #1
0
def handle_lighting():
    """Handles the lighting state management."""

    status_led = RGBLED(13, 19, 26)
    steps = 100
    current_step = 0

    while not QUIT_EVENT.is_set():
        if GPS_STATUS in GPSStatus.locked_states():
            set_rgb_colour(status_led, Colour.GREEN)
            sleep(1)
        else:
            current_step = (current_step + 1) % steps
            cycle_rgb_led(status_led, current_step, steps)
            sleep(1 / steps)

    status_led.off()
    status_led.close()
Exemple #2
0
class PyDensha:
    def __init__(self, led_pins=None):
        self._led = None

        self.assign_led(led_pins)

    @_ignore_exception
    def assign_led(self, led_pins):
        self._close_led()

        try:
            self._led = RGBLED(red=led_pins.get('red'),
                               green=led_pins.get('green'),
                               blue=led_pins.get('blue'))
        except PinInvalidPin:
            pass

    @_ignore_exception
    def _close_led(self):
        self._led.close()

    @_ignore_exception
    def operate_led(self, train_infos, on_time=1, off_time=1):
        def _are_all_trains_operate_normally(train_infos):
            return all(train_info == '平常運転' for train_info in train_infos)

        def _is_all_train_either_operate_normally_or_delayed(train_infos):
            return all(train_info == '平常運転' or train_info == '遅延'
                       for train_info in train_infos)

        if None not in train_infos:
            if _are_all_trains_operate_normally(train_infos):
                self._led.color = Color('green')
            elif _is_all_train_either_operate_normally_or_delayed(train_infos):
                self._led.blink(on_time=on_time,
                                off_time=off_time,
                                on_color=Color('yellow'))
            else:
                self._led.blink(on_time=on_time,
                                off_time=off_time,
                                on_color=Color('red'))
Exemple #3
0
class PiPadLED:

    # initialize
    def __init__(self):

        # set PINs on BOARD
        log.debug("Initializing LEDs...")
        log.debug("> RED pin: " + str(_conf['red_pin']))
        log.debug("> GREEN pin: " + str(_conf['green_pin']))
        log.debug("> BLUE pin: " + str(_conf['blue_pin']))
        self.rgbled = RGBLED(_conf['red_pin'], _conf['green_pin'],
                             _conf['blue_pin'])
        log.debug("...init done!")

    # off
    def off(self):
        log.debug("Set to off")
        self.rgbled.off()

    # on
    def on(self):
        log.debug("Set to on")
        self.color()

    # set color
    def color(self, color="white", time=0):
        log.debug("Set to " + color + " color for " + str(time) + "s")
        self.rgbled.color = Color(color)
        if not time == 0:
            sleep(time)
            self.rgbled.off()

    # terminate
    def terminate(self):
        log.debug("LEDs termination...")
        self.rgbled.close()
  subprocess.run(['raspistill', '-n', '-t', '2s', '-o', '/tmp/out.jpeg'], check=True)
  subprocess.run(['convert', '/tmp/out.jpeg', '-resize', '128x64!', '/tmp/small.jpeg'])
  img = Image.open('/tmp/small.jpeg').convert('1')
  screen.display_image(img)


def release():
  led.off()

with open('db_config.json') as inf:
  dbconfig = json.load(inf)

repo = TemperatureRepository(**dbconfig)

temp_reader = BME280()
lux_reader = TSL2561()
button.when_pressed = button_press
button.when_released = release

try:
  while True:
    temp = temp_reader.read()
    repo.insert_temp(temp)
    lux = lux_reader.read()
    repo.insert_lux(lux)
    button.wait_for_press(300)
except KeyboardInterrupt:
  button.close()
  led.close()

Exemple #5
0
class CHFSM:

    # Normal operation
    IDLE_COLOUR = (1.5, 1.5, (0, 0, 0), (1.0, 1.0, 1.0))  # off to White
    CH_ONLY_COLOUR = (1.5, 1.5, (0, 0, 0), (0, 0.9, 0.4)
                      )  # off to Reddy-purple
    HW_ONLY_COLOUR = (1.5, 1.5, (0, 0, 0), (0.9, 0.0, 0.1)
                      )  # off to Greeny-cyan
    CH_AND_HW_COLOUR = (1.5, 1.5, (0, 0, 0), (0.9, 0.1, 0.0))  # off to Orange
    # INHIBIT_MODE = (1.5, 1.5, (0,0,0), (1, 0, 0.5))  # Off to purple

    # Errors
    NO_GATEWAY_CONNECTION = (1.5, 1.5, (1, 0, 0), (0.9, 0, 0.9)
                             )  # Red to purple
    SCHEDULE_MISSING = (1.5, 1.5, (1, 0, 0), (1, 0.3, 0.0)
                        )  # red to yellow/orange
    BAD_SCHEDULE = (1.5, 1.5, (1, 0, 0), (1, 1, 0))  # red to cyan

    # Maintainance mode
    BLUETOOTH_MODE = (1.5, 1.5, (0, 0, 1), (0, 1, 1))  # blue to cyan
    UPDATING = (1.5, 1.5, (0, 0, 1), (0, 1, 0))  #blue to Green

    update_time = config[r"update_time"]

    # Update and reboot time (day_of_month, hour, minute, second)
    UPDATE_REBOOT_TIME = (update_time["day_of_month"], update_time["hour"],
                          update_time["minute"], update_time["second"])

    PULSE_UP_TIME = 1.5
    PULSE_DOWN_TIME = 1.5

    BUTTON_BOUNCE_TIME = None

    # The duration of a CH or HW boost, in seconds
    BOOST_DURATION = config["boost_duration"]

    def __init__(self, red, green, blue, hw_relay, ch_relay, hw_button,
                 ch_button, rgb_active_high, ch_relay_active_high,
                 hw_relay_active_high):

        # Setup push buttons
        self.ch_button = BetterButton(ch_button, self.chPressed,
                                      self.BUTTON_BOUNCE_TIME, None, True)
        self.hw_button = BetterButton(hw_button, self.hwPressed,
                                      self.BUTTON_BOUNCE_TIME, None, True)

        # Setup relay
        self.hw_relay = DigitalOutputDevice(hw_relay, hw_relay_active_high)
        self.ch_relay = DigitalOutputDevice(ch_relay, ch_relay_active_high)

        # Setup RGB status LED
        self.status_led = RGBLED(red, green, blue, active_high=rgb_active_high)

        self.commands_file_path = config["commands_path"]
        self.schedule_file_path = config["schedule_path"]

        # Setup HW boost objects
        self.hw_boost_start = None
        self.ch_boost_on = False

        self.hw_pressed_flag = False
        self.ch_pressed_flag = False

        # Load first state
        self.state = IdleState(self)
        self.state.enter()

        #
        self.last_gateway_ping = datetime.now()

    def close(self):
        self.status_led.close()
        self.hw_relay.close()
        self.ch_relay.close()

    def setState(self, state):
        self.state.leave()
        self.state = state
        logging.debug("State change: %s", type(state))
        self.state.enter()

    def process(self):
        self.state.process()

    def getState(self):
        return self.state

    def chPressed(self):
        logging.debug("CH button press")
        self.ch_pressed_flag = True

    def hwPressed(self):
        logging.debug("HW button press")
        self.hw_pressed_flag = True

    def setStatusLed(self, colour):
        self.status_led.pulse(colour[0],
                              colour[1],
                              off_color=colour[2],
                              on_color=colour[3])

    def parent_folder():
        return os.path.dirname(os.path.dirname(__file__))
                self.lock.acquire()
                self.color = self.colors_list[self.cycle_count
                                              % len(self.colors_list)]
                self.cycle_count = self.cycle_count + 1
                self.lock.release()

                # Set the color for the LED
                RGB_LED.color = Color(self.color.lower())

                # Display the color for specified interval before switching again
                time.sleep(self.interval_ms/1000)

            # If button is pressed, display the current color for 5 seconds
            elif not self.keep_cycling and self.game_active:
                time.sleep(5)
                RGB_LED.off()
                self.lock.acquire()
                self.game_active = False
                self.lock.release()
            else:
                RGB_LED.off()
                time.sleep(0.1)


if __name__ == '__main__':
    try:
        ColorCyclerGadget().main()
    finally:
        RGB_LED.close()
        BUTTON.close()
class GpiorgbcontrollerPlugin(octoprint.plugin.StartupPlugin,
							  octoprint.plugin.SettingsPlugin,
                              octoprint.plugin.AssetPlugin,
                              octoprint.plugin.TemplatePlugin,
							  octoprint.plugin.SimpleApiPlugin):

	def __init__(self):
		self.led = None
		self.color = '#FFFFFF'
		self.is_on = False
		self.btn = None
		self.is_btn_en = False
		self.gcode_command_enable = False
		self.gcode_index_enable = False
		self.gcode_rgb_index = 1
		self.pin_factory = RPiGPIOFactory()
	

	def init_rgb(self, red_pin, grn_pin, blu_pin):
		try:
			self.deinit_rgb()
			self.led = RGBLED(red=red_pin, green=grn_pin, blue=blu_pin, active_high=True, pin_factory=self.pin_factory)
			self._logger.info("LEDs initialized with pin factory: " + str(self.led.pin_factory))
		except:
			self._logger.error("Error occurred while initializing LEDs")


	def deinit_rgb(self):
		try:
			if(self.led is not None):
				self.led.close()
				self.led = None
				self._logger.info("LEDs deinitialized")
		except:
			self._logger.error("Error occurred while deinitializing LEDs")


	def init_btn(self, pin):
		try:
			self.deinit_btn()
			self.btn = Button(pin, pin_factory=self.pin_factory)
			self.btn.when_pressed = self.on_btn_press
			self.btn.when_released = self.on_btn_release
			self._logger.info("Button initialized with pin factory: " + str(self.btn.pin_factory) )
		except:
			self._logger.error("Error occurred while initializing button")


	def deinit_btn(self):
		try:
			if(self.btn is not None):
				self.btn.close()
				self.btn = None
				self._logger.info("Button deinitialized")
		except:
			self._logger.error("Error occurred while deinitializing button")


	def read_btn(self):
		if(self.btn is not None and self.is_btn_en):
			if(self.btn.is_pressed):
				self.on_btn_press()
			else:
				self.on_btn_release()


	def on_btn_press(self):
		if(self.is_btn_en):
			self.is_on = True
			self.update_rgb(self.color, self.is_on)


	def on_btn_release(self):
		if(self.is_btn_en):
			self.is_on = False
			self.update_rgb(self.color, self.is_on)
			

	def update_rgb(self, color, is_on):
		if(self.led is not None):
			rgb = int(color[1:], 16)
			red = ((rgb & 0xFF0000) >> 16) / 255
			grn = ((rgb & 0x00FF00) >> 8) / 255
			blu = ((rgb & 0x0000FF)) / 255
			if is_on:
				self.led.color = (red, grn, blu)
			else:
				self.led.color = (0, 0, 0)
		else:
			self._logger.error("Error occurred while updating RGB state")


	def on_after_startup(self):
		red_pin = self._settings.get_int(["red_pin"])
		grn_pin = self._settings.get_int(["grn_pin"])
		blu_pin = self._settings.get_int(["blu_pin"])
		color = self._settings.get(["color"])
		is_on = self._settings.get_boolean(["is_on"])
		if red_pin is not None and grn_pin is not None and blu_pin is not None:
			self.init_rgb(red_pin, grn_pin, blu_pin)
			if(is_on is not None and color is not None):
				self.color = color
				self.is_on = is_on
				self.update_rgb(self.color, self.is_on)
		btn_pin = self._settings.get_int(["btn_pin"])
		is_btn_en = self._settings.get_boolean(["is_btn_en"])
		if btn_pin is not None and is_btn_en is not None:
			self.init_btn(btn_pin)
			self.is_btn_en = is_btn_en
			self.read_btn()
		gcode_command_enable = self._settings.get_boolean(["gcode_command_enable"])
		if gcode_command_enable is not None:
			self.gcode_command_enable = gcode_command_enable
		gcode_index_enable = self._settings.get_boolean(["gcode_index_enable"])
		if gcode_index_enable is not None:
			self.gcode_index_enable = gcode_index_enable
		gcode_rgb_index = self._settings.get_int(["gcode_rgb_index"])
		if gcode_rgb_index is not None:
			self.gcode_rgb_index = gcode_rgb_index
		self._plugin_manager.send_plugin_message(self._identifier, dict(is_on=self.is_on, color=self.color))
		

	def on_settings_save(self, data):
		octoprint.plugin.SettingsPlugin.on_settings_save(self, data)
		if 'red_pin' in data or 'grn_pin' in data or 'blu_pin' in data:
			red_pin = self._settings.get_int(["red_pin"])
			grn_pin = self._settings.get_int(["grn_pin"])
			blu_pin = self._settings.get_int(["blu_pin"])
			if(red_pin is not None and grn_pin is not None and blu_pin is not None):
				self.init_rgb(red_pin, grn_pin, blu_pin)
		color = self._settings.get(["color"])
		is_on = self._settings.get_boolean(["is_on"])
		if is_on is not None and color is not None:
			self.color = color
			self.is_on = is_on
			self.update_rgb(self.color, self.is_on)
		if 'btn_pin' in data or 'is_btn_en' in data:
			btn_pin = self._settings.get_int(["btn_pin"])
			is_btn_en = self._settings.get_boolean(["is_btn_en"])
			if(btn_pin is not None and is_btn_en is not None):
				self.init_btn(btn_pin)
				self.is_btn_en = is_btn_en
				self.read_btn()
		gcode_command_enable = self._settings.get_boolean(["gcode_command_enable"])
		if gcode_command_enable is not None:
			self.gcode_command_enable = gcode_command_enable
		gcode_index_enable = self._settings.get_boolean(["gcode_index_enable"])
		if gcode_index_enable is not None:
			self.gcode_index_enable = gcode_index_enable
		gcode_rgb_index = self._settings.get_int(["gcode_rgb_index"])
		if gcode_rgb_index is not None:
			self.gcode_rgb_index = gcode_rgb_index
		
	def get_settings_defaults(self):
		return dict(
			red_pin=20,
			grn_pin=25,
			blu_pin=5,
			color='#FFFFFF',
			is_on = False,
			btn_pin=21,
			is_btn_en=False,
			gcode_command_enable=False,
			gcode_index_enable=False,
			gcode_rgb_index=1,
		)


	def get_assets(self):
		# Define your plugin's asset files to automatically include in the
		# core UI here.
		return dict(
			js=["js/gpiorgbcontroller.js",  "js/jscolor.min.js"],
			css=["css/gpiorgbcontroller.css"],
			less=["less/gpiorgbcontroller.less"]
		)


	def get_template_configs(self):
		return [
			dict(type="settings", custom_bindings=False)
		]


	def get_api_commands(self):
		return dict(
			update_color=["color"],
			turn_on=[],
			turn_off=[]
		)


	def on_api_command(self, command, data):
		if command == "update_color":
			color = data.get('color', None)
			if color != None:
				self.color = color
		elif command == "turn_on":
			self.is_on = True
		elif command == "turn_off":
			self.is_on = False
		self.update_rgb(self.color, self.is_on)
		

	def gcode_parse_index(self, cmd):
		params = cmd.split("I")
		if len(params) != 2:
			return None
		else:
			try:
				index = int(params[1].split()[0])
				if index > 0:
					return index
				else:
					return None
			except:
				return None


	def gcode_parse_rgb_component(self, cmd, comp):
		try:
			comp_type = comp.upper()
			if comp_type != "R" and comp_type != "G" and comp_type != "B":
				return None
			delim = ""
			if comp_type == "R":
				delim = "R"
			elif comp_type == "G":
				delim = "U"
			elif comp_type == "B":
				delim = "B"
			params = cmd.split(delim)
			if len(params) != 2:
				return None
			else:
				color_int = int(params[1].split()[0])
				if color_int < 0 or color_int > 255:
					return None
				else:
					color_hex = "%02X" % color_int
					if len(color_hex) != 2:
						return None
					else:
						return color_hex
		except:
			return None


	def replace_color_component(self, color, value, comp):
		if len(color) != 7 or value is None:
			return color
		comp_type = comp.upper()
		if comp_type != "R" and comp_type != "G" and comp_type != "B":
			return color
		if len(value) != 2:
			return color
		old_r = color[1:3]
		old_g = color[3:5]
		old_b = color[5:7]
		new_color = color
		if comp_type == "R":
			new_color = "#" + value + old_g + old_b
		elif comp_type == "G":
			new_color = "#" + old_r + value + old_b
		elif comp_type == "B":
			new_color = "#" +old_r + old_g + value
		return new_color


	def on_gcode_command(self, comm_instance, phase, cmd, cmd_type, gcode, *args, **kwargs):
		if not self.gcode_command_enable:
			return 
		if gcode and gcode.startswith("M150"):
			color = "#000000"
			red = self.gcode_parse_rgb_component(cmd, "R")
			color = self.replace_color_component(color, red, "R")
			grn = self.gcode_parse_rgb_component(cmd, "G")
			color = self.replace_color_component(color, grn, "G")
			blu = self.gcode_parse_rgb_component(cmd, "B")
			color = self.replace_color_component(color, blu, "B")
			if self.gcode_index_enable:
				index = self.gcode_parse_index(cmd)
				if index is not None and index == self.gcode_rgb_index and color is not None and len(color) == 7:
					self.color = color
					self.is_on = True
					self.update_rgb(self.color, self.is_on)
					self._plugin_manager.send_plugin_message(self._identifier, dict(is_on=self.is_on, color=self.color))
			else:
				if color is not None and len(color) == 7:
					self.color = color
					self.is_on = True
					self.update_rgb(self.color, self.is_on)
					self._plugin_manager.send_plugin_message(self._identifier, dict(is_on=self.is_on, color=self.color))
		

	def get_update_information(self):
		# Define the configuration for your plugin to use with the Software Update
		# Plugin here. See https://docs.octoprint.org/en/master/bundledplugins/softwareupdate.html
		# for details.
		return dict(
			gpiorgbcontroller=dict(
				displayName="Gpiorgbcontroller Plugin",
				displayVersion=self._plugin_version,

				# version check: github repository
				type="github_release",
				user="******",
				repo="OctoPrint-GpioRgbController",
				current=self._plugin_version,

				# update method: pip
				pip="https://github.com/z4gunn/OctoPrint-GpioRgbController/archive/{target_version}.zip"
			)
		)
Exemple #8
0
class AlertHarness:
    def __init__(self, args):
        if args.test or args.replay:
            self.radio = None
        else:
            r = self.radio = rf95.RF95(cs=RF95_SPI_CS, int_pin=RF95_IRQ_PIN, reset_pin=RF95_RESET_PIN)
            assert r.init(), "failed to intialize radio"
            r.set_modem_config(rf95.Bw125Cr45Sf128)
            r.set_frequency(RF95_FREQ)

        self._args = args

        self.log_handle = open(args.log, 'w') if args.log else None
        self.replay_handle = open(args.replay, 'r') if args.replay else None
        self._pending_line = None
        self._time_offset = 0

        if self.replay_handle:
            if self.read_replay_line():
                self._time_offset = time.time() - self._pending_line[0]

        self.test = args.test
        self.use_tts = args.tts
        self.use_alarm = args.alarm
        self.numbers = args.phone
        self.emails = args.email

        self.stopping = False
        self.led = RGBLED(*RGBLED_PINS)

        self.lcd_thread = LCDThread(SEGMENT_PINS, DIGIT_PINS)

        self.trouble_tags = set()
        self.known_tags = set()

        self.monitor_thread = BackgroundThread(self.status_loop, start=False)
        self.announce_thread = None
        self._buzzer = False

        if twilio is not None:
            self.twilio_client = TwilioClient(TWILIO_SID, TWILIO_AUTH)
        else:
            self.twilio_client = None

        if sendgrid is not None:
            self.mail_client = sendgrid.SendGridAPIClient(apikey=SENDGRID_API_KEY)
        else:
            self.mail_client = None

        self.conn = None
        self.states = StateTracker()

    def read_replay_line(self) -> bool:
        try:
            line = next(self.replay_handle)
            ts, rssi, pkt_hex = line.split(",")
            ts = float(ts.strip())
            rssi = float(rssi.strip())
            pkt = bytes.fromhex(pkt_hex.strip())
            self._pending_line = ts, rssi, pkt
            return True
        except (StopIteration, ValueError):
            self._pending_line = None
            return False

    def set_plotter(self, address: str, port: int = PLT_DEFAULT_PORT):
        if isinstance(port, str):
            port = int(port)
        elif port is None:
            port = PLT_DEFAULT_PORT

        self.conn = mpClient((address, port))

    def send_sms(self, body: str):
        if self.twilio_client is None or not self.numbers:
            return False

        ret = []
        for number in self.numbers:
            msg = self.twilio_client.messages.create(to=number, from_=TWILIO_NUMBER, body=body)
            ret.append(msg)

        return ret

    def send_email(self, subject: str, body: str):
        if self.mail_client is None or not self.emails:
            return False

        personalization = Personalization()

        for email in self.emails:
            email = Email(email)
            personalization.add_to(email)

        from_email = Email(SENDGRID_FROM_ADDRESS)
        content = Content("text/plain", body)
        mail = Mail(from_email, subject, None, content)
        mail.add_personalization(personalization)
        return self.mail_client.client.mail.send.post(request_body=mail.get())

    def get_packet(self, timeout=None) -> Optional[RadioPacket]:
        packet = None

        if self.test:
            return
        elif self.replay_handle:
            packet = self.replay_packet(timeout)
        elif self.radio:
            start = time.time()

            while not self.radio.available():
                sleep(0.005)
                if timeout and (time.time() - start > timeout):
                    return

            rxbuf = self.radio.recv()
            rxbuf = bytes(rxbuf[4:])  # skip flag
            rssi = self.radio.last_rssi

            try:
                packet = RadioPacket.from_bytes(rxbuf, rssi, timestamp=time.time())
            except Exception as e:
                h = rxbuf.hex()
                hexstr = ' '.join(h[x:x + 2] for x in range(0, len(h), 2))
                new_e = RuntimeError("error parsing packet with length %d and data:\n%s"
                                     % (len(rxbuf), hexstr))
                raise new_e from e

        if packet:
            self.states.update(packet)
            return packet

    def replay_packet(self, timeout=None):
        if self._pending_line:
            target = self._pending_line[0] + self._time_offset
            sleep_for = target - time.time()

            if timeout and sleep_for > timeout:
                sleep(timeout)
                return None
            else:
                sleep(max(0, sleep_for))
                timestamp, rssi, rxbuf = self._pending_line
                timestamp = target

                if not self.read_replay_line():
                    print("end of log, stopping")
                    self.stopping = True

                return RadioPacket.from_bytes(rxbuf, rssi, timestamp=timestamp)

    def run_test(self):
        print("Test mode: will simulate an alert in 10 seconds")
        sleep(10)
        self.show_alert(42)
        print("Test mode: returning to normal in 10 seconds")
        sleep(10)
        self.clear_alert()
        print("Test mode: will exit in 5 seconds")
        sleep(5)

    def run(self):
        # Initialize everything as OK
        self.clear_alert()
        self.monitor_thread.start()
        self.lcd_thread.start()

        if self.test:
            return self.run_test()

        while not self.stopping:
            try:
                packet = self.get_packet(timeout=1)

                if not packet:
                    continue

                if self.conn:
                    self.conn.send((packet.raw, packet.rssi, packet.timestamp))

                if self.log_handle:
                    line = '%s,%s,%s' % (packet.timestamp, packet.rssi, packet.raw.hex())
                    self.log_handle.write(line + '\n')

                # print(packet.serial, packet.seq, packet.orientation, packet.rssi, packet.vbatt)
                # pprint(packet.samples[-1])

                self.known_tags.add(packet.tag)

                if self.states.state_for(packet.tag) is State.DISTRESS:
                    if packet.tag not in self.trouble_tags:
                        self.trouble_tags.add(packet.tag)
                        self.show_alert(packet.tag)
                elif packet.tag in self.trouble_tags:
                    self.trouble_tags.remove(packet.tag)
                    self.clear_alert()

            except Exception:
                traceback.print_exc()

    def status_loop(self):
        while not self.stopping:
            for i in range(int(ACTIVE_SLEEP / 0.01)):
                sleep(0.01)

                if self.stopping:
                    break

            tag_strs = ['%d: %s' % (t, self.states.state_for(t).name.lower())
                        for t in self.known_tags]
            print("Active tags:\n%s" % ('\n'.join(tag_strs) or '(none)'))

    def show_alert(self, tag: int):
        print("showing alert for tag", tag)
        self.lcd_thread.set_value(tag)
        self.led.pulse(on_color=RED)

        msg = "Tag %s is in distress!" % tag
        self.announce_msg = msg
        self.announce()

        self.send_email("Bovine Intervention alert", msg)
        self.send_sms("Bovine Intervention alert: " + msg)

    def clear_alert(self):
        print("clearing alert")
        self.led.blink(on_color=GREEN, on_time=0.05, off_time=5 - 0.05)
        self.lcd_thread.clear()
        self.announce_msg = None

        if self.announce_thread:
            self.announce_thread.stop()  # block

    def announce_cb(self, result):
        if self.announce_msg is None or self.stopping:
            # reset to None
            self.announce_thread = None
            self._buzzer = False
        elif self._buzzer:
            self._buzzer = False
            self.announce_thread = CommandThread([TTS_SCRIPT, self.announce_msg], self.announce_cb)
        else:
            self._buzzer = True
            self.announce_thread = CommandThread(["mplayer", "-really-quiet", ALARM_SOUND], self.announce_cb)

    def announce(self):
        if self.announce_thread:
            self.announce_thread.join()  # block

        self.announce_thread = CommandThread(["mplayer", ALARM_SOUND], self.announce_cb)
        self._buzzer = True

    def shutdown(self):
        self.stopping = True
        self.lcd_thread.stop()
        self.monitor_thread.join()
        self.led.close()
        #GPIO.cleanup()

        if self.announce_thread:
            self.announce_thread.stop()

        if self.radio:
            self.radio.cleanup()

        if self.log_handle:
            self.log_handle.close()

        if self.replay_handle:
            self.replay_handle.close()