def test_database_output_plugin(self): """ Test that the DatabaseOutputPlugin works as expected. """ plugin = monitor.DatabaseOutputPlugin( 'sqlite://', interval=timedelta(seconds=1)) # # in-memory database plugin.initialise() example_400010_line = b'R 0.024: 400010010081013700180015001A000000000000400000 CRC=87' monitor.parse_line(example_400010_line, [plugin]) with sql.session_scope(plugin.engine) as session: self.assertEqual( 0, session.query(sql.P1P2State).count(), "Plugin should not write to database until 1 second after initialisation" ) monitor.parse_line(example_400010_line, [plugin]) with sql.session_scope(plugin.engine) as session: self.assertEqual( 0, session.query(sql.P1P2State).count(), "Plugin should not write to database until 1 second after initialisation" ) time.sleep(1) monitor.parse_line(example_400010_line, [plugin]) with sql.session_scope(plugin.engine) as session: self.assertEqual( 1, session.query(sql.P1P2State).count(), "Second line should write values accumulated during first round " "to the database") record = session.query(sql.P1P2State).one() self.assertEqual(55, record.target_dhw_temp) monitor.parse_line(example_400010_line, [plugin]) with sql.session_scope(plugin.engine) as session: self.assertEqual( 1, session.query(sql.P1P2State).count(), "Plugin should not write to database until 1 second after last write" ) time.sleep(1) monitor.parse_line(example_400010_line, [plugin]) with sql.session_scope(plugin.engine) as session: self.assertEqual( 2, session.query(sql.P1P2State).count(), "Second line should write values accumulated during first round " "to the database")
def poll_once(daikin_interface, decoding_table, engine): """ Poll Daikin ASHP once for all known pages. Record their raw contents and decoded values in the database. """ with session_scope(engine) as session: serial_state = SerialState( timestamp=datetime.datetime.now().astimezone(), raw_page_contents={}, variable_values={}) session.add(serial_state) for prefix in decoding_table: command_packet = bytes(prefix) + pytherma.comms.calculate_checksum( prefix) response_packet = pytherma.comms.execute_command( daikin_interface, command_packet) serial_state.raw_page_contents[prefix[2]] = list( response_packet[3:-1]) values = decoding.decode_using_table(command_packet, response_packet, decoding_table) # JSON dict keys are always strings, so for the avoidance of doubt, we convert # the keys to strings here. https://stackoverflow.com/questions/1450957 serial_state.variable_values.update({ str(variable_id): _adapt_value_to_json(value[1]) for variable_id, value in values.items() })
def test_poll_once(self): """ Test that poll_once() works as expected using built-in definitions. """ simulator = DaikinSimulator() engine = sqlalchemy.create_engine('sqlite://') # in-memory database sql.Base.metadata.create_all(engine) poller.poll_once(simulator, decoding.serial_page_prefix_to_decoders, engine) with sql.session_scope(engine) as session: self.assertEqual(1, session.query(sql.SerialState).count()) state = session.query(sql.SerialState).one() # The simulator returns a response at random when there's more than one, so choose # a value to which the response is always the same: self.assertEqual(0x2, state.variable_values['47'], "Wrong decode for 47:O/U EEPROM (1st digit)") self.assertEqual(0x31, state.variable_values['48'], "Wrong decode for 48:O/U EEPROM (3rd 4th digit)") self.assertEqual(0x95, state.variable_values['49'], "Wrong decode for 49:O/U EEPROM (5th 6th digit)") self.assertEqual(0x1, state.variable_values['50'], "Wrong decode for 50:O/U EEPROM (7th 8th digit)") self.assertEqual(0x2, state.variable_values['51'], "Wrong decode for 51:O/U EEPROM (10th digit)") self.assertEqual(0x5, state.variable_values['52'], "Wrong decode for 52:O/U EEPROM (11th digit)")
def handle_parsed_values(self, values, raw_record): """ Handle some values parsed by decode_p1p2, by collecting them ready to write to the database. """ if self.record is None: self.record = P1P2State(raw_packets_contents={}) # Store the most recent packet for each prefix in raw_packets_contents: packet_prefix = raw_record[:6] self.record.raw_packets_contents[packet_prefix] = raw_record for name, (decoder, value) in values.items(): if name.value not in P1P2State.__mapper__.attrs: raise AttributeError( f"{name} is not a valid attribute of P1P2State") setattr(self.record, name.value, value) time_now = datetime.datetime.now().astimezone() if time_now > self.next_write_time: # Write values collected so far to the database, and clear them. self.record.timestamp = time_now with session_scope(self.engine) as session: session.add(self.record) self.record = None self.next_write_time += self.interval
def test_espaltherma_definitions(self): """ Test that poll_once() works as expected using espaltherma definitions. """ simulator = DaikinSimulator() definitions_file = os.path.join(DEFINITION_FILES, 'ALTHERMA(LT_CA_CB_04-08KW).h') with open(definitions_file) as f: definition_text = f.read() espaltherma_decoding_table = parse_espaltherma_definition( definition_text, output_text=False) engine = sqlalchemy.create_engine('sqlite://') # in-memory database sql.Base.metadata.create_all(engine) poller.poll_once(simulator, espaltherma_decoding_table, engine) with sql.session_scope(engine) as session: self.assertEqual(1, session.query(sql.SerialState).count()) state = session.query(sql.SerialState).one() # The simulator returns a response at random when there's more than one, so choose # a value to which the response is always the same: self.assertEqual(6.0, state.variable_values['00.12.105'], "21:O/U capacity (kW)")