def setup_minimal_master_platform(port): # type: (str) -> None config = ConfigParser() config.read(constants.get_config_file()) platform = Platform.get_platform() Injectable.value(controller_serial=Serial(port, 115200)) if platform == Platform.Type.DUMMY: Injectable.value(maintenance_communicator=None) Injectable.value(master_controller=MasterDummyController()) elif platform in Platform.CoreTypes: from master.core import ucan_communicator _ = ucan_communicator core_cli_serial_port = config.get('OpenMotics', 'cli_serial') Injectable.value(cli_serial=Serial(core_cli_serial_port, 115200)) Injectable.value(master_communicator=CoreCommunicator()) Injectable.value(maintenance_communicator=None) Injectable.value(memory_file=MemoryFile()) Injectable.value(master_controller=MasterCoreController()) elif platform in Platform.ClassicTypes: Injectable.value( eeprom_db=constants.get_eeprom_extension_database_file()) from master.classic import eeprom_extension _ = eeprom_extension Injectable.value(master_communicator=MasterCommunicator()) Injectable.value(maintenance_communicator=None) Injectable.value(master_controller=MasterClassicController()) else: logger.warning('Unhandled master implementation for %s', platform)
def _register_background_consumers(self): if self._master_version and not self._background_consumers_registered: if Platform.get_platform() == Platform.Type.CLASSIC: # This import/code will eventually be migrated away to MasterControllers from master.master_communicator import BackgroundConsumer self._master_communicator.register_consumer( BackgroundConsumer( master_api.shutter_status(self._master_version), 0, self._on_shutter_update)) self._background_consumers_registered = True
def get_state(self): authorized_mode = self._authorized_mode if Platform.get_platform() == Platform.Type.CORE_PLUS: authorized_mode = True # TODO: Should be handled by actual button return { 'run_gpio': self._last_run_gpio, 'run_i2c': self._last_run_i2c, 'run_buttons': self._last_button_check, 'run_state_check': self._last_state_check, 'authorized_mode': authorized_mode }
def __init__( self, master_communicator=INJECTED): # type: (CoreCommunicator) -> None super(FrontpanelCoreController, self).__init__() self._master_communicator = master_communicator self._master_communicator.register_consumer( BackgroundConsumer(CoreAPI.event_information(), 0, self._handle_event)) self._led_states = {} # type: Dict[str, LedStateTracker] self._led_event_lock = Lock() self._carrier = True self._connectivity = True self._activity = False self._cloud = False self._vpn = False self._led_drive_states = {} # type: Dict[str, Tuple[bool, str]] self._check_buttons_thread = None self._authorized_mode_buttons = [False, False] self._authorized_mode_buttons_pressed_since = None # type: Optional[float] self._authorized_mode_buttons_released = False self._platform = Platform.get_platform()
if __name__ == '__main__': setup_logger() config = ConfigParser() config.read(constants.get_config_file()) Injectable.value(config_db_lock=Lock()) Injectable.value(config_db=constants.get_config_database_file()) Injectable.value(eeprom_db=constants.get_eeprom_extension_database_file()) controller_serial_port = config.get('OpenMotics', 'controller_serial') Injectable.value(controller_serial=Serial(controller_serial_port, 115200)) from gateway import config as config_controller _ = config_controller if Platform.get_platform() == Platform.Type.CORE_PLUS: from gateway.hal import master_controller_core # type: ignore from master_core import maintenance, core_communicator, ucan_communicator # type: ignore _ = master_controller_core, maintenance, core_communicator, ucan_communicator # type: ignore else: from gateway.hal import master_controller_classic # type: ignore from master import maintenance, master_communicator, eeprom_extension # type: ignore _ = master_controller_classic, maintenance, master_communicator, eeprom_extension # type: ignore lock_file = constants.get_init_lockfile() if os.path.isfile(lock_file): with open(lock_file) as fd: content = fd.read() if content == 'factory_reset': logger.info('Running factory reset...')
""" import sqlite3 import logging import time import pytz from datetime import datetime from croniter import croniter from random import randint from threading import Thread from ioc import Injectable, Inject, INJECTED, Singleton from platform_utils import Platform from gateway.webservice import params_parser import ujson as json if Platform.get_platform() == Platform.Type.CLASSIC: from master.master_communicator import CommunicationTimedOutException else: # TODO: Replace for the Core+ class CommunicationTimedOutException(Exception): # type: ignore pass logger = logging.getLogger('openmotics') class Schedule(object): timezone = None def __init__(self, id, name, start, repeat, duration, end, schedule_type,
def get_master_type(): if Platform.get_platform() in Platform.CoreTypes: return 'master_coreplus' else: return 'master_classic'
def main(): # type: () -> None """ The main function. """ parser = argparse.ArgumentParser(description='Tool to control the master.') parser.add_argument('--port', dest='port', action='store_true', help='get the serial port device') parser.add_argument('--sync', dest='sync', action='store_true', help='sync the serial port') parser.add_argument('--reset', dest='reset', action='store_true', help='reset the master') parser.add_argument('--hard-reset', dest='hardreset', action='store_true', help='perform a hardware reset on the master') parser.add_argument('--version', dest='version', action='store_true', help='get the version of the master') parser.add_argument('--wipe', dest='wipe', action='store_true', help='wip the master eeprom') parser.add_argument('--update', dest='update', action='store_true', help='update the master firmware') parser.add_argument('--master-firmware-classic', help='path to the hexfile with the classic firmware') parser.add_argument('--master-firmware-core', help='path to the hexfile with the core+ firmware') args = parser.parse_args() setup_logger() config = ConfigParser() config.read(constants.get_config_file()) port = config.get('OpenMotics', 'controller_serial') if args.port: print(port) return if not any([args.sync, args.version, args.reset, args.hardreset, args.wipe, args.update]): parser.print_help() setup_minimal_master_platform(port) platform = Platform.get_platform() if args.hardreset: master_cold_reset() return elif args.update: if platform in Platform.CoreTypes: firmware = args.master_firmware_core if not firmware: print('error: --master-firmware-core is required to update') sys.exit(1) else: firmware = args.master_firmware_classic if not firmware: print('error: --master-firmware-classic is required to update') sys.exit(1) master_update(firmware) return communicator = get_communicator() communicator.start() try: if args.sync: master_sync() elif args.version: master_version() elif args.reset: master_reset() elif args.wipe: master_factory_reset() finally: communicator.stop()
def main(): supported_modules = ['O', 'R', 'D', 'I', 'T', 'C'] supported_modules_gen3 = ['O3', 'R3', 'D3', 'I3', 'T3', 'C3'] supported_can_modules = ['UC'] all_supported_modules = supported_modules + supported_modules_gen3 + supported_can_modules parser = argparse.ArgumentParser( description='Tool to bootload the slave modules.') parser.add_argument( '-t', '--type', dest='type', choices=all_supported_modules + [m.lower() for m in all_supported_modules], required=True, help='The type of module to bootload (choices: {0})'.format( ', '.join(all_supported_modules))) parser.add_argument('-f', '--file', dest='file', required=True, help='The filename of the hex file to bootload') parser.add_argument('-v', '--version', dest='version', required=False, help='The version of the firmware to flash') parser.add_argument('--verbose', dest='verbose', action='store_true', help='Show the serial output') args = parser.parse_args() module_type = args.type.upper() filename = args.file version = args.version gen3_firmware = module_type.endswith('3') if gen3_firmware: module_type = module_type[0] config = ConfigParser() config.read(constants.get_config_file()) port = config.get('OpenMotics', 'controller_serial') setup_minimal_master_platform(port) communicator = get_communicator() communicator.start() try: if Platform.get_platform() in Platform.CoreTypes: from master.core.slave_updater import SlaveUpdater update_success = SlaveUpdater.update_all( module_type=module_type, hex_filename=filename, gen3_firmware=gen3_firmware, version=version) else: from master.classic.slave_updater import bootload_modules try: if os.path.getsize(args.file) <= 0: print('Could not read hex or file is empty: {0}'.format( args.file)) return False except OSError as ex: print('Could not open hex: {0}'.format(ex)) return False if module_type == 'UC': print( 'Updating uCAN modules not supported on Classic platform') return True # Don't fail the update update_success = bootload_modules(module_type=module_type, filename=filename, gen3_firmware=gen3_firmware, version=version) finally: communicator.stop() time.sleep(3) return update_success
def setup_platform(message_client_name): # type: (Optional[str]) -> None setup_target_platform(Platform.get_platform(), message_client_name)
def start(self): """ Starts the monitoring thread """ self._ensure_gateway_api() if Platform.get_platform() == Platform.Type.CLASSIC: self._thread.start()
def build_graph(): config = ConfigParser() config.read(constants.get_config_file()) config_lock = Lock() scheduling_lock = Lock() metrics_lock = Lock() config_database_file = constants.get_config_database_file() # TODO: Clean up dependencies more to reduce complexity # IOC announcements # When below modules are imported, the classes are registerd in the IOC graph. This is required for # instances that are used in @Inject decorated functions below, and is also needed to specify # abstract implementations depending on e.g. the platform (classic vs core) or certain settings (classic # thermostats vs gateway thermostats) from power import power_communicator, power_controller from plugins import base from gateway import (metrics_controller, webservice, scheduling, observer, gateway_api, metrics_collector, maintenance_controller, comm_led_controller, users, pulses, config as config_controller, metrics_caching, watchdog) from cloud import events _ = (metrics_controller, webservice, scheduling, observer, gateway_api, metrics_collector, maintenance_controller, base, events, power_communicator, comm_led_controller, users, power_controller, pulses, config_controller, metrics_caching, watchdog) if Platform.get_platform() == Platform.Type.CORE_PLUS: from gateway.hal import master_controller_core from master_core import maintenance, core_communicator, ucan_communicator from master import eeprom_extension # TODO: Obsolete, need to be removed _ = master_controller_core, maintenance, core_communicator, ucan_communicator else: from gateway.hal import master_controller_classic from master import maintenance, master_communicator, eeprom_extension _ = master_controller_classic, maintenance, master_communicator, eeprom_extension thermostats_gateway_feature = Feature.get_or_none( name='thermostats_gateway') thermostats_gateway_enabled = thermostats_gateway_feature is not None and thermostats_gateway_feature.enabled if Platform.get_platform( ) == Platform.Type.CORE_PLUS or thermostats_gateway_enabled: from gateway.thermostat.gateway import thermostat_controller_gateway _ = thermostat_controller_gateway else: from gateway.thermostat.master import thermostat_controller_master _ = thermostat_controller_master # IPC Injectable.value(message_client=MessageClient('openmotics_service')) # Cloud API parsed_url = urlparse(config.get('OpenMotics', 'vpn_check_url')) Injectable.value(gateway_uuid=config.get('OpenMotics', 'uuid')) Injectable.value(cloud_endpoint=parsed_url.hostname) Injectable.value(cloud_port=parsed_url.port) Injectable.value(cloud_ssl=parsed_url.scheme == 'https') Injectable.value(cloud_api_version=0) # User Controller Injectable.value(user_db=config_database_file) Injectable.value(user_db_lock=config_lock) Injectable.value(token_timeout=3600) Injectable.value( config={ 'username': config.get('OpenMotics', 'cloud_user'), 'password': config.get('OpenMotics', 'cloud_pass') }) # Configuration Controller Injectable.value(config_db=config_database_file) Injectable.value(config_db_lock=config_lock) # Energy Controller power_serial_port = config.get('OpenMotics', 'power_serial') Injectable.value(power_db=constants.get_power_database_file()) if power_serial_port: Injectable.value(power_serial=RS485( Serial(power_serial_port, 115200, timeout=None))) else: Injectable.value(power_serial=None) Injectable.value(power_communicator=None) Injectable.value(power_controller=None) # Pulse Controller Injectable.value(pulse_db=constants.get_pulse_counter_database_file()) # Scheduling Controller Injectable.value( scheduling_db=constants.get_scheduling_database_file()) Injectable.value(scheduling_db_lock=scheduling_lock) # Master Controller controller_serial_port = config.get('OpenMotics', 'controller_serial') Injectable.value( controller_serial=Serial(controller_serial_port, 115200)) if Platform.get_platform() == Platform.Type.CORE_PLUS: from master_core.memory_file import MemoryFile, MemoryTypes core_cli_serial_port = config.get('OpenMotics', 'cli_serial') Injectable.value(cli_serial=Serial(core_cli_serial_port, 115200)) Injectable.value(passthrough_service=None) # Mark as "not needed" Injectable.value( memory_files={ MemoryTypes.EEPROM: MemoryFile(MemoryTypes.EEPROM), MemoryTypes.FRAM: MemoryFile(MemoryTypes.FRAM) }) # TODO: Remove; should not be needed for Core Injectable.value( eeprom_db=constants.get_eeprom_extension_database_file()) else: passthrough_serial_port = config.get('OpenMotics', 'passthrough_serial') Injectable.value( eeprom_db=constants.get_eeprom_extension_database_file()) if passthrough_serial_port: Injectable.value( passthrough_serial=Serial(passthrough_serial_port, 115200)) from master.passthrough import PassthroughService _ = PassthroughService # IOC announcement else: Injectable.value(passthrough_service=None) # Metrics Controller Injectable.value(metrics_db=constants.get_metrics_database_file()) Injectable.value(metrics_db_lock=metrics_lock) # Webserver / Presentation layer Injectable.value(ssl_private_key=constants.get_ssl_private_key_file()) Injectable.value(ssl_certificate=constants.get_ssl_certificate_file())
def _migrate(cls, master_controller=INJECTED ): # type: (MasterClassicController) -> None # Core(+) platforms never had non-ORM rooms if Platform.get_platform() in Platform.CoreTypes: return # Import legacy code @Inject def _load_eeprom_extension(eeprom_extension=INJECTED): # type: (EepromExtension) -> EepromExtension return eeprom_extension eext_controller = _load_eeprom_extension() from master.classic.eeprom_models import (OutputConfiguration, InputConfiguration, SensorConfiguration, ShutterConfiguration, ShutterGroupConfiguration, PulseCounterConfiguration) rooms = {} # type: Dict[int, Room] floors = {} # type: Dict[int, Floor] # Rooms and floors logger.info('* Rooms & floors') for room_id in range(100): try: RoomsMigrator._get_or_create_room(eext_controller, room_id, rooms, floors, skip_empty=True) except Exception: logger.exception('Could not migrate single RoomConfiguration') # Main objects for eeprom_model, orm_model, filter_ in [ (OutputConfiguration, Output, lambda o: True), (InputConfiguration, Input, lambda i: i.module_type in ['i', 'I']), (SensorConfiguration, Sensor, lambda s: True), (ShutterConfiguration, Shutter, lambda s: True), (ShutterGroupConfiguration, ShutterGroup, lambda s: True) ]: logger.info('* {0}s'.format(eeprom_model.__name__)) try: for classic_orm in master_controller._eeprom_controller.read_all( eeprom_model): try: object_id = classic_orm.id if object_id is None: continue if not filter_(classic_orm): RoomsMigrator._delete_eext_fields( eext_controller, eeprom_model.__name__, object_id, ['room']) continue try: room_id = int( RoomsMigrator._read_eext_fields( eext_controller, eeprom_model.__name__, object_id, ['room']).get('room', 255)) except ValueError: room_id = 255 object_orm, _ = orm_model.get_or_create( number=object_id) # type: ignore if room_id == 255: object_orm.room = None else: object_orm.room = RoomsMigrator._get_or_create_room( eext_controller, room_id, rooms, floors) object_orm.save() RoomsMigrator._delete_eext_fields( eext_controller, eeprom_model.__name__, object_id, ['room']) except Exception: logger.exception('Could not migrate single {0}'.format( eeprom_model.__name__)) except Exception: logger.exception('Could not migrate {0}s'.format( eeprom_model.__name__)) # PulseCounters pulse_counter = None # type: Optional[PulseCounter] # - Master try: logger.info('* PulseCounters (master)') for pulse_counter_classic_orm in master_controller._eeprom_controller.read_all( PulseCounterConfiguration): try: pulse_counter_id = pulse_counter_classic_orm.id try: room_id = int( RoomsMigrator._read_eext_fields( eext_controller, 'PulseCounterConfiguration', pulse_counter_id, ['room']).get('room', 255)) except ValueError: room_id = 255 pulse_counter = PulseCounter.get_or_none( number=pulse_counter_id) if pulse_counter is None: pulse_counter = PulseCounter( number=pulse_counter_id, name=pulse_counter_classic_orm.name, persistent=False, source=u'master') else: pulse_counter.name = pulse_counter_classic_orm.name pulse_counter.persistent = False pulse_counter.source = u'master' if room_id == 255: pulse_counter.room = None else: pulse_counter.room = RoomsMigrator._get_or_create_room( eext_controller, room_id, rooms, floors) pulse_counter.save() RoomsMigrator._delete_eext_fields( eext_controller, 'PulseCounterConfiguration', pulse_counter_id, ['room']) except Exception: logger.exception( 'Could not migrate classic master PulseCounter') except Exception: logger.exception('Could not migrate classic master PulseCounters') # - Old SQLite3 old_sqlite_db = constants.get_pulse_counter_database_file() if os.path.exists(old_sqlite_db): try: logger.info('* PulseCounters (gateway)') import sqlite3 connection = sqlite3.connect( old_sqlite_db, detect_types=sqlite3.PARSE_DECLTYPES, check_same_thread=False, isolation_level=None) cursor = connection.cursor() for row in cursor.execute( 'SELECT id, name, room, persistent FROM pulse_counters ORDER BY id ASC;' ): try: pulse_counter_id = int(row[0]) room_id = int(row[2]) pulse_counter = PulseCounter.get_or_none( number=pulse_counter_id) if pulse_counter is None: pulse_counter = PulseCounter( number=pulse_counter_id, name=str(row[1]), persistent=row[3] >= 1, source=u'gateway') else: pulse_counter.name = str(row[1]) pulse_counter.persistent = row[3] >= 1 pulse_counter.source = u'gateway' if room_id == 255: pulse_counter.room = None else: pulse_counter.room = RoomsMigrator._get_or_create_room( eext_controller, room_id, rooms, floors) pulse_counter.save() except Exception: logger.exception( 'Could not migratie gateway PulseCounter') os.rename(old_sqlite_db, '{0}.bak'.format(old_sqlite_db)) except Exception: logger.exception('Could not migrate gateway PulseCounters')