Menu główne

Natywna integracja z Home Assistant (komponent) 2.2

Zaczęty przez admin, Wrzesień 20, 2022, 22:35:33

Poprzedni wątek - Następny wątek

w3t3ran

#225
Niestety u mnie podobna sytuacja , proszę o pomoc w przywróceniu funkcjonalności.

EDIT 26.05.2024 r.: Przywróciłem pełną kopię zapasową z 19.05.2024 r. (Taką miałem przed awarią), zaktualizowałem i wszystko działa poprawnie. Przepraszam za zamieszanie.

Łukasz

Cytat: rafaniem@gmail.com w Maj 21, 2024, 20:49:16
Ktoś może pomóc? Kontroler widzi, ale nic więcej (błąd konfiguracji)
Rejestrator: homeassistant.config_entries
Źródło: config_entries.py:575
Pierwsze zdarzenie: 20:22:28 (1 zdarzenia)
Ostatnio zalogowany: 20:22:28

Error setting up entry Exta for extalife
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 575, in async_setup
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 189, in async_setup_entry
    return await initialize(hass, config_entry)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 303, in initialize
    await data.async_start_polling(poll_now=True)
  File "/config/custom_components/extalife/__init__.py", line 365, in async_start_polling
    await self.async_execute_status_polling()
  File "/config/custom_components/extalife/__init__.py", line 372, in async_execute_status_polling
    await self._async_update_callback()
  File "/config/custom_components/extalife/__init__.py", line 392, in _async_update_callback
    channels = await self.controller.async_get_channels()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 492, in async_get_channels
    channels.extend(self._get_channels_int(resp))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 568, in _get_channels_int
    ch_no = state.get("channel", def_channel) if def_channel else state["channel"]      # pylint: disable=unused-variable
                                                                  ~~~~~^^^^^^^^^^^
KeyError: 'channel'

U mnie wersja z https://github.com/dgtal1/extalife_custom_component plus poprawka w tym pull request https://github.com/dgtal1/extalife_custom_component/pull/38 naprawiła sytuację.

Łukasz

Cytat: benio w Maj 30, 2024, 09:22:46
Witam .

Dzisiaj ściągnąłem całą integrację z posta powyżej oraz zastosowałem zmianę jak w tym poscie.

Niestety cały czas integracja wywalała błąd konfiguracji. Włączyłem rejestrowanie debugowania w integracji i magia integracja uruchomiła się. Może ta informacja da jakiś trop. Dodatkowo przesyłam pliki debugu .

Ja miałem jeszcze zabawę z efc-1. Jak łączyłem się aplikacją to dostawałem komunikat że hasło nie działa. Zresetowałem hasło na takie jak było, ale też przywróciłem z backup. I proszę usuń logi które wstawiłeś bo jest tam podane twoje hasło do EFC-1

w3t3ran

Cytat: Łukasz w Maj 29, 2024, 23:24:28
Cytat: rafaniem@gmail.com w Maj 21, 2024, 20:49:16
Ktoś może pomóc? Kontroler widzi, ale nic więcej (błąd konfiguracji)
Rejestrator: homeassistant.config_entries
Źródło: config_entries.py:575
Pierwsze zdarzenie: 20:22:28 (1 zdarzenia)
Ostatnio zalogowany: 20:22:28

Error setting up entry Exta for extalife
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 575, in async_setup
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 189, in async_setup_entry
    return await initialize(hass, config_entry)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 303, in initialize
    await data.async_start_polling(poll_now=True)
  File "/config/custom_components/extalife/__init__.py", line 365, in async_start_polling
    await self.async_execute_status_polling()
  File "/config/custom_components/extalife/__init__.py", line 372, in async_execute_status_polling
    await self._async_update_callback()
  File "/config/custom_components/extalife/__init__.py", line 392, in _async_update_callback
    channels = await self.controller.async_get_channels()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 492, in async_get_channels
    channels.extend(self._get_channels_int(resp))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 568, in _get_channels_int
    ch_no = state.get("channel", def_channel) if def_channel else state["channel"]      # pylint: disable=unused-variable
                                                                  ~~~~~^^^^^^^^^^^
KeyError: 'channel'

U mnie wersja z https://github.com/dgtal1/extalife_custom_component plus poprawka w tym pull request https://github.com/dgtal1/extalife_custom_component/pull/38 naprawiła sytuację.

Z ciekawości (bo nie musiałem - integracja działa) zaimplementowałem te zmiany i wszystko nadal funkcjonuje
bardzo dobrze. Dziękuję  :)

Gadulowaty

Bo ta linia jest jakąś pozostałością po nie wiadomo czym, przypisuje do zmiennej ch_no wartość która nie jest później nigdzie wykorzystywana. Zdecydowanie do wykomentowania jest cała linia, a najlepiej do usunięcia.

Co do haseł to jedną z wytycznych dla twórców integracji jest zakaz zapisywania haseł do logu. W swoim forku na githubie wprowadziłem zmianę do kodu która rozpoznaje czy jest podłączony debugger  i tylko wtedy zrzuca hasła do logu w innym wypadku zamienia je na "*" ( Link tutaj )

W tym forku znajdują się również już poprawki dotyczące ostrzeżeń które się dotyczą elementów oznaczonych w kolejnych wersjach do usunięcia lub zmiany. Jestem w kontakcie z adminem i mam nadzieję że nie długo przygotuję pull request, żeby te zmiany trafiły do głównego repozytorium. (Muszę jeszcze poprawić ten problem)

W kolejnym etapie dodam obsługę nowego urządzenia RCW-21

A w wolnych chwilach (których mam jak na lekarstwo) szykuje urządzenie (ESP32) do przechwytywania bezpośrednio komunikacji radiowej i wypychanie jej poprzez komunikaty MQTT do integracji - docelowo zamiana poll na push. ( :) tak tak jestem w stanie odbierać i wysyłać komunikaty radiowe systemu ExtaLife).

Na razie,
Gadulowaty
Produkcja:RPi5,EFC-01; Sensory:MEM-21,RCK-21,9xRCM-21,2xRCR-21,RCW-21; Odbiorniki: 2xROB-21, 5xROG-21,11xROP-21,11xROP-22,1xRDP-21,11xSRP-22; Nadajniki: 2xP-457/36,4xP-457/4,P-457/8,P-521L,1xRNK-22,1xRNK-24 | Developer:RPi4,EFC-01,RCK-21,ROB-21,ROP-22,ROG-21,RCM-21

w3t3ran

Cytat: Gadulowaty w Maj 30, 2024, 13:03:24
Bo ta linia jest jakąś pozostałością po nie wiadomo czym, przypisuje do zmiennej ch_no wartość która nie jest później nigdzie wykorzystywana. Zdecydowanie do wykomentowania jest cała linia, a najlepiej do usunięcia.

Co do haseł to jedną z wytycznych dla twórców integracji jest zakaz zapisywania haseł do logu. W swoim forku na githubie wprowadziłem zmianę do kodu która rozpoznaje czy jest podłączony debugger  i tylko wtedy zrzuca hasła do logu w innym wypadku zamienia je na "*" ( Link tutaj )

W tym forku znajdują się również już poprawki dotyczące ostrzeżeń które się dotyczą elementów oznaczonych w kolejnych wersjach do usunięcia lub zmiany. Jestem w kontakcie z adminem i mam nadzieję że nie długo przygotuję pull request, żeby te zmiany trafiły do głównego repozytorium. (Muszę jeszcze poprawić ten problem)

W kolejnym etapie dodam obsługę nowego urządzenia RCW-21

A w wolnych chwilach (których mam jak na lekarstwo) szykuje urządzenie (ESP32) do przechwytywania bezpośrednio komunikacji radiowej i wypychanie jej poprzez komunikaty MQTT do integracji - docelowo zamiana poll na push. ( :) tak tak jestem w stanie odbierać i wysyłać komunikaty radiowe systemu ExtaLife).

Na razie,
Gadulowaty

Świetna wiadomość, kibicuję mocno i już nie mogę się doczekać. Pozdrawiam

Cezary.K

@Gadulowaty - świetna robota, już grzeję wodę na kawę dla Ciebie 8). Jestem pod wielkim wrażeniem Twoich umiejętności. Z obawą patrząc na starzejący się kawałek elektroniki w postaci EFC-01 w mim domu (8 rok leci), chętnie przetestuję sniffer Twojego konceptu. Straciłam już nadzieję na ujrzenie wyników Twoich dociekań, badań nad tym protokołem radiowym. Tym bardziej się cieszę. Myślę, że będzie nawet pokusa o rozszerzenie swojego ekosystemu urządzeń ExtaLife zamiast ułomnych Zigbee. Jeśli możesz tylko dodać dwa słowa, co sprzętowo będę potrzebował? Jakaś płytka radia z SX oprócz ESP32 pewnie będzie potrzebna. W co się uzbroić najlepiej?
Elementy systemu, których używam: Bramka AIS DEV3 "Jolka" + pilot+aplikacja WearOS, 32 urządzenia Exta Life z EFC-01, Mi-flora, Sonoff, ESP8266, ESP32 (ESPHome, Tasmota) Zigbee2mqtt, kamery IP, inteligentny głośnik w oparciu o AIS DEV1, urządzenia SIP.

benio

Cytat: Łukasz w Maj 30, 2024, 11:06:10
Cytat: benio w Maj 30, 2024, 09:22:46
Witam .

Dzisiaj ściągnąłem całą integrację z posta powyżej oraz zastosowałem zmianę jak w tym poscie.

Niestety cały czas integracja wywalała błąd konfiguracji. Włączyłem rejestrowanie debugowania w integracji i magia integracja uruchomiła się. Może ta informacja da jakiś trop. Dodatkowo przesyłam pliki debugu .

Ja miałem jeszcze zabawę z efc-1. Jak łączyłem się aplikacją to dostawałem komunikat że hasło nie działa. Zresetowałem hasło na takie jak było, ale też przywróciłem z backup. I proszę usuń logi które wstawiłeś bo jest tam podane twoje hasło do EFC-1

Dziękuję usunąłem wiadomość

admin

#233
Cytat: rafaniem@gmail.com w Maj 21, 2024, 20:49:16
Ktoś może pomóc? Kontroler widzi, ale nic więcej (błąd konfiguracji)
Rejestrator: homeassistant.config_entries
Źródło: config_entries.py:575
Pierwsze zdarzenie: 20:22:28 (1 zdarzenia)
Ostatnio zalogowany: 20:22:28

Error setting up entry Exta for extalife
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 575, in async_setup
    result = await component.async_setup_entry(hass, self)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 189, in async_setup_entry
    return await initialize(hass, config_entry)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/__init__.py", line 303, in initialize
    await data.async_start_polling(poll_now=True)
  File "/config/custom_components/extalife/__init__.py", line 365, in async_start_polling
    await self.async_execute_status_polling()
  File "/config/custom_components/extalife/__init__.py", line 372, in async_execute_status_polling
    await self._async_update_callback()
  File "/config/custom_components/extalife/__init__.py", line 392, in _async_update_callback
    channels = await self.controller.async_get_channels()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 492, in async_get_channels
    channels.extend(self._get_channels_int(resp))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/extalife/pyextalife.py", line 568, in _get_channels_int
    ch_no = state.get("channel", def_channel) if def_channel else state["channel"]      # pylint: disable=unused-variable
                                                                  ~~~~~^^^^^^^^^^^
KeyError: 'channel'
Kilka pytań:
1. Skąd dokładnie pobrałeś integrację? Podaj link którego użyłeś. To dość nietypowy i mało prawdopodobny błąd. Wygląda jakby odpowiedź z kontrolera byłą niepoprawna.

2. Jaką masz wersję oprogramowania kontrolera? Instalowałeś coś nowego ostatnio? Jakoś nie chce mi się wierzyć, żeby Zamel zmienił protokół komunikacji z aplikacją Exta Life, więc albo masz jakąś dziwną wersję integracji, albo jest jakiś poważny błąd w jej kodzie, którego dotąd nikt nie doświadczył.

3. Czy integracja działała i nagle przestałą czy to był pierwszy raz gdy próbowałeś ją uruchomić w HA?
Elementy systemu, których używam: 3xSRP-22, 5xRDP-21, 3xROP-22, 4x ROP-21, 1xP-456/36, 1xRNK-24, 3x RNP-21, 1xRCT-22, aplikacja na Android oraz iOS + Home Assistant

Gadulowaty

Admin po co sie zastanawiac - ta linia jest do usuniecia. Przeanalizuj kod zmienna ch_no nie jest nigdzie uzywana. w wersji nad ktora pracuje usunalem to i nie ma problemu.
A dla podniesienia napiecia - to dzisiaj wieczorem a najpóźniej jutro rano wrzuce posta ze zmianami jakie przez ostatnie dwa tygodnie przygotowałem.

Na razie,
Gadulowaty
Produkcja:RPi5,EFC-01; Sensory:MEM-21,RCK-21,9xRCM-21,2xRCR-21,RCW-21; Odbiorniki: 2xROB-21, 5xROG-21,11xROP-21,11xROP-22,1xRDP-21,11xSRP-22; Nadajniki: 2xP-457/36,4xP-457/4,P-457/8,P-521L,1xRNK-22,1xRNK-24 | Developer:RPi4,EFC-01,RCK-21,ROB-21,ROP-22,ROG-21,RCM-21

benio

Panowie. Czy te zmiany które są będą działać z najnowsza wersja HA ? Bo nie wiem czy aktualizować. Pozdrawiam.

Gadulowaty

#236
@Benio: Jeżeli chodzi o zgodność z HA to będzie ok - ja mam to puszczone z wersją 2024.5.5 i hula na produkcyjnym systemie. Bardziej niepokoi mnie problem który pojawił się u @rafaniem (2-3 posty wcześniej). Tam jest jakiś problem z komunikacją efc. Jeżeli wystąpi u ciebie ten problem w pliku pyextalife.py w lini 568 zamień z:

ch_no = state.get("channel", def_channel) if def_channel else state["channel"]      # pylint: disable=unused-variable

na

#  ch_no = state.get("channel", def_channel) if def_channel else state["channel"]      # pylint: disable=unused-variable

I będzie git :). Ale ostrzegam, że przy ładowaniu integracji będzie w logu sypać ostrzeżeniami o wygasającej zgodności z przyszłymi wersjami - z tego co pamiętam najbliższa "breaking change" będzie w wersji 2024.9.
A jak nie boisz się zostać pilotem-oblatywaczem to zapraszam na
@gadulowaty GitHub. Piloci Kamikaze mile widziani :) :) :)

Na razie,
Gadulowaty
Produkcja:RPi5,EFC-01; Sensory:MEM-21,RCK-21,9xRCM-21,2xRCR-21,RCW-21; Odbiorniki: 2xROB-21, 5xROG-21,11xROP-21,11xROP-22,1xRDP-21,11xSRP-22; Nadajniki: 2xP-457/36,4xP-457/4,P-457/8,P-521L,1xRNK-22,1xRNK-24 | Developer:RPi4,EFC-01,RCK-21,ROB-21,ROP-22,ROG-21,RCM-21

Cezary.K

Cytat: Gadulowaty w Czerwiec 14, 2024, 08:29:00

A jak nie boisz się zostać pilotem-oblatywaczem to zapraszam na
@gadulowaty GitHub. Piloci Kamikaze mile widziani :) :) :)

Na razie,
Gadulowaty

@Gadulowaty - czy masz konto na buycoffee.to? Chętnie postawił bym CI kawę.

Gdybyś odpowiedział mi na wcześniejsze pytania odnoście sprzętu radiowego pod ESP32 sniffer ExtaLife. Chętnie będę pilotem oblatywaczem tego rozwiązania i poprawionej integracji z HA.
Swojego EFC-01 staram się nie dotykać, nie aktualizować.
Elementy systemu, których używam: Bramka AIS DEV3 "Jolka" + pilot+aplikacja WearOS, 32 urządzenia Exta Life z EFC-01, Mi-flora, Sonoff, ESP8266, ESP32 (ESPHome, Tasmota) Zigbee2mqtt, kamery IP, inteligentny głośnik w oparciu o AIS DEV1, urządzenia SIP.

admin

#238
Cytat: Gadulowaty w Maj 30, 2024, 13:03:24
Bo ta linia jest jakąś pozostałością po nie wiadomo czym, przypisuje do zmiennej ch_no wartość która nie jest później nigdzie wykorzystywana. Zdecydowanie do wykomentowania jest cała linia, a najlepiej do usunięcia.

Co do haseł to jedną z wytycznych dla twórców integracji jest zakaz zapisywania haseł do logu. W swoim forku na githubie wprowadziłem zmianę do kodu która rozpoznaje czy jest podłączony debugger  i tylko wtedy zrzuca hasła do logu w innym wypadku zamienia je na "*" ( Link tutaj )

W tym forku znajdują się również już poprawki dotyczące ostrzeżeń które się dotyczą elementów oznaczonych w kolejnych wersjach do usunięcia lub zmiany. Jestem w kontakcie z adminem i mam nadzieję że nie długo przygotuję pull request, żeby te zmiany trafiły do głównego repozytorium. (Muszę jeszcze poprawić ten problem)

W kolejnym etapie dodam obsługę nowego urządzenia RCW-21

A w wolnych chwilach (których mam jak na lekarstwo) szykuje urządzenie (ESP32) do przechwytywania bezpośrednio komunikacji radiowej i wypychanie jej poprzez komunikaty MQTT do integracji - docelowo zamiana poll na push. ( :) tak tak jestem w stanie odbierać i wysyłać komunikaty radiowe systemu ExtaLife).

Na razie,
Gadulowaty

Po przesiadce na nowego laptopa zapomniałem włączyć push dla forum i proszę, co tu się odjaniepawla  ;)
Jestem również żywo zainteresowany takim snifferem - z resztą pisałem już o tym wielokrotnie. EFC-01 najchętniej schowałbym gdzieś głęboko do szafy, bo to złom do którego muszę zaglądać, rozbierać i przywracać kartę SD niemal po każdym braku zasilania.
Jeśli taki sniffer mógłby zastąpić EFC-01 i nadawać po MQTT to byłbym szczęśliwy. Chętnie się przesiądę. Prawdę mówiąc to tak jak Cezary.K tez straciłem nadzieję, ale dobrze, że jednak coś tam dalej kombinujesz, to może w końcu coś z tego wyjdzie. Niestety tutaj nie jestem w stanie pomóc w żaden sposób bo mikrokontrolery to nie moja działka.

Cytat: Gadulowaty w Czerwiec 14, 2024, 09:21:52
A co do urządzenia do sniffera to zastanawiałem się na tym gadżetem. Ma prawie wszystko poza ethernetem - nie odpowiada mi koncepcja po WiFi. W domu u mnie wszystkie urządzenia stale działające w sieci preferują kabel. WiFi na noc przez Putina wyłączam dla oszczędności prądu.

Ostatecznej decyzji jeszcze nie ma. Żeby się coś wyklarowało to rozważam otwarcie nowego wątku na forum dot. czego właściwie oczekujemy od tego urządzenia. Potrzebuję pomysłów i oczekiwanych wymagań wtedy przystąpię do realizacji projektu.

Pozostaje jeszcze problem dystrybucji klucza do szyfrowania komunikacji. Wg. obecnego stanu nie chcę go ujawniać bo jest on wspólny dla wszystkich instalacji. Załóżmy sytuację że ktoś z takim urządzeniem i znający klucz do odszyfrowania transmisji zjawia się pod waszym domem. Resztę domyślcie się sami....
Jak dla mnie WiFi jest wystarczająco OK. Kabel oczywiście lepszy i odporny na restarty rutera itp, ale umówmy się - to są rzadkie przypadki, a nikt przy zdrowych zmysłach nie postawiłby chyba krytycznych urządzeń wykonawczych na Exta Life, więc nie widzę problemu.

A co do klucza - to może nie trzeba go w ogóle ujawniać? Wystarczy gdybyś upichcił gotowy, skompilowany soft, a my byśmy go sobie tylko załadowali do płytek i wtedy klucz pozostaje nadal tajny. Jeśli ktoś potrafi go wyciągnąć z kodu to i tak sobie spokojnie poradzi z wyciągnięciem go z EFC-01, więc status quo pozostaje w tej kwestii.

Jak będziesz gotowy z tym PR ze wszystkimi zmianami - daj znać. Trzeba wydać betę do testów, potem odczekać jakiś czas i zobaczyć feedback od użytkowników i wydać wersję stabilną, a potem dodawać kolejne rzeczy, wydać kolejną betę, stabilną itd Krok po kroku :)

PS. No i wielkie dzięki, że przyłączyłeś się do współtworzenia integracji. I to jak! :)
Elementy systemu, których używam: 3xSRP-22, 5xRDP-21, 3xROP-22, 4x ROP-21, 1xP-456/36, 1xRNK-24, 3x RNP-21, 1xRCT-22, aplikacja na Android oraz iOS + Home Assistant

Gadulowaty

#239
@Admin, co do sniffera to nie wymagam wskazówek jak to napisać tylko CO napisać, chodzi mi o funkcjonalności.
Tematu sniffera nie porzuciłem tylko ostatnie 2-3 lata świat zawodowy mi "zawirował" i dopiero teraz wszystko ustawiłem sobie po nowemu (zmarł kolega z działu z którym pracowałem od 2004r.). Cały czas mam z tyłu głowy projekt stworzenia urządzenia w oparaciu o symulację 2xROM-24 na ESP32 - razem 8 kanałów do sterowania podlewajką ogrodową. Nie widzę innej koncepcji, gdyż alternatywa to użycie 8xROP-21 - mają niezależne wyjścia sterujące a podlewajka jest na 24VAC. To rozwiązanie mi nie pasuje bo w  obecnym systemie mam już 66 urządzeń i zbliżam się do granicy wydolności EFC (80 urządzeń) i dodanie kolejnych 8 nie wchodzi w grę.

Ten wczorajszy commit jest na tyle stabilny, że u siebie wrzuciłem już na produkcję. Dopasowania mogą wymagać zmienione timeouty na connect i await przy receivie poleceń. Możesz chyba zacząć mergować. Podpowiedz mi tylko bo widzę że ten PR co puściłem cały czas zwiększa się o nowe moje commity - jak zrobić żeby się odciąć od tamtego PR i zacząć nowy PR ?

Na razie,
Gadulowaty
Produkcja:RPi5,EFC-01; Sensory:MEM-21,RCK-21,9xRCM-21,2xRCR-21,RCW-21; Odbiorniki: 2xROB-21, 5xROG-21,11xROP-21,11xROP-22,1xRDP-21,11xSRP-22; Nadajniki: 2xP-457/36,4xP-457/4,P-457/8,P-521L,1xRNK-22,1xRNK-24 | Developer:RPi4,EFC-01,RCK-21,ROB-21,ROP-22,ROG-21,RCM-21