Raspberri PI: прием и передача сигналов на частоте 433mhz

На даче у меня управление батареями построено на реле Sonoff R3 DIY, которые включаются-выключаются при помощи WiFi. Это было моей ошибкой — управление получилось не очень стабильным. Реле имеют свойство периодически «отваливаться», из-за того что сигнал от роутера слабоват и весь дом покрывает с трудом. Решил освоить управление реле при помощи приёмопередатчиков 433mhz. Приём-передача по слухам гораздо стабильнее. Да и пультики-брелки можно будет прикрутить таким образом для управления уличным освещением. Приятно будет — подъезжаешь вечером на машине и шарахаться в темноте не нужно — с пульта включил свет. Плюс бонусом хочу на первый этаж поставить arduino с приёмником, и часть функционала с raspberry перекинуть на неё, т.к. тупо пинов уже не хватает к малинке.

» Читать далее

Dart: секунды в строку вида hh:mm:ss

Задача: преобразовать и отобразить секунды в строку часы-минуты-секунды

Решение:

  String TimeLeft(diff){
    var timestamp = diff.floor();
    var hours = (timestamp / 60 / 60).floor();
    var minutes = ((timestamp / 60) - (hours * 60)).floor();
    var seconds = timestamp % 60;
    if (seconds<0){
      seconds=0;hours=0;minutes=0;
    }
    String hs=hours.toString();
    hs=hs.length>1?hs:"0"+hs;
    String ms=minutes.toString();
    ms=ms.length>1?ms:"0"+hs;
    String ss=seconds.toString();
    ss=ss.length>1?ss:"0"+ss;
    return "$hs:$ms:$ss";
  }

Бонусом тоже самое на javascript:

function TimeLeft(diff){    
    var timestamp = Math.floor(diff);
    var hours = Math.floor(timestamp / 60 / 60);
    var minutes = Math.floor(timestamp / 60) - (hours * 60);
    var seconds = timestamp % 60;        
    if (seconds<0){seconds=0;hours=0;minutes=0;} 
    h=("0"+hours).slice(-2);
    m=("0"+minutes).slice(-2);
    s=("0"+seconds).slice(-2);
};

Flutter: использование радиокнопок

Обычно радиокнопки во Flutter обертываю виджетом ListTile. Однако в этом случае получаются слишком большие отступы между кнопками, которые обычными средствами не убираются. В моем варианте, кнопки размещаются просто в Row строке:

enum ChangingTime {t30a,t30,t60,t90,t120,t150,t180}
...
ChangingTime? _chtime = ChangingTime.t30a;
...
Container RadioLine(String title,ChangingTime rvalue){
    return
    Container(
      padding: const EdgeInsets.only(bottom: 0,left: 0,right: 0,top: 0),
      height: 20,
      child:
        Row(
          children: [
            Expanded(
                child: Padding(
                  padding: const EdgeInsets.only(bottom: 0,left: 8,right: 8,top: 0),
                  child: Text(title),
                )
            ),
            Radio<ChangingTime>(
              value: rvalue,
              groupValue: _chtime,
              onChanged: (ChangingTime? value) {
                setState(() {
                  _chtime = value;
                  print(value);
                });
              },
            ),
          ],
        )
      );
  }
...
...
RadioLine("30 минут с автопродлением",ChangingTime.t30a),
new Divider(),
RadioLine("30 минут",ChangingTime.t30),
new Divider(),
RadioLine("1 час",ChangingTime.t60),

Получается вполне приемлимое:

Одно но. В таком случае выбор кнопки будет только по щелчку по переключателю, а не по тексту. Как вариант можно попробовать RadioListTile, но с ним отступы до желаемого минимума мне убрать не удалось.

 RadioListTile RadioLine2(String title,ChangingTime rvalue){
    return
      RadioListTile<ChangingTime>(
        title:Text(
            title,
        ),
        dense: true,
        value: rvalue,
        groupValue: _chtime,
        visualDensity: VisualDensity(
          horizontal: VisualDensity.minimumDensity,
          vertical: VisualDensity.minimumDensity,
        ),
        controlAffinity: ListTileControlAffinity.trailing,
        onChanged: (ChangingTime? value) {
            setState(() {
                _chtime = value;
                print(value);
            });
        },
      );
  }

Flutter: древовидный список с анимацией

Задача: показать двухуровневый список с анимацией отображения выбора второго уровня.

Что сделаем? Отображением займутся два вложенных друг в друга ListView.builder. Отлавливает выбор модели при помощи GestureDetector и при отрисовке второго ListView.builder со списокм автомобилей, показываем только совпадающие с моделью. Также отображение моделей обертываем в AnimatedOpacity для плавного показа.

class _AddAutoState extends State<AddAuto> {
  final AddAuto_scaffoldKey = GlobalKey<ScaffoldState>();
  bool auto_list_is_loading=false;
  List ListAutoes=[];
  List FilteredListAutoes=[];
  int cur_model_select=0;
  void LoadListAutoes(context){
    TRequests req=new TRequests();
    req.request("GetListVendorsAndModels", jsonEncode({}), (List result){
      setState(() {
        ListAutoes=result;
        FilteredListAutoes=result;
        auto_list_is_loading=true;
      });
    }, (String error){
      EasyLoading.showToast(error);
    });
  }

  @override
  void initState() {
    WidgetsBinding.instance.addPostFrameCallback((_) => LoadListAutoes(context));  // эвент после того как страница отобразилась - обновим данные по пользователю
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        key: AddAuto_scaffoldKey,
        appBar: AppBar(
          iconTheme: IconThemeData(color: Colors.black),
          backgroundColor: Colors.white,
          //automaticallyImplyLeading: false, // убрать кнопку "назад"
          title: Text('Добавление автомобиля', style: TextStyle(fontFamily: 'Poppins', color: Colors.black, fontSize: 22,),),
          actions: [],
          centerTitle: false,
          elevation: 2,
        ),
        body:
        SingleChildScrollView(
          child:
          Column(
            children: [
              Visibility(
                  visible: auto_list_is_loading,
                  child:
                  Container(
                    padding: const EdgeInsets.all(8),
                    child:
                    ListView.builder(
                        shrinkWrap: true,
                        physics: NeverScrollableScrollPhysics(),
                        padding: const EdgeInsets.all(0),
                        itemCount: FilteredListAutoes.length,
                        itemBuilder: (BuildContext context, int index) {
                          return GestureDetector(
                              onTap: () {
                                setState(() {
                                  print("тыкнули по модели автомобиля..");
                                  cur_model_select=index;
                                });
                              },
                              child:
                              Column(
                                  children: [
                                    Row(
                                      mainAxisSize: MainAxisSize.max,
                                      mainAxisAlignment: MainAxisAlignment.start,
                                      children: [
                                        Container(
                                          height: 16,
                                          width: 16,
                                          child:
                                            Transform.rotate(
                                              angle: index==cur_model_select?90 * 3.14/180:0,
                                              child:
                                                IconButton (
                                                  padding: const EdgeInsets.all(0),
                                                  icon: SvgPicture.asset('lib/images/arrow-right-small.svg',height: 16,width: 16,),
                                                  onPressed: () {print('IconButton pressed ...');},
                                                ),
                                            )
                                        ),
                                        Text(
                                            FilteredListAutoes[index]["name"]+" ("+FilteredListAutoes[index]["cars"].length.toString()+")",
                                            style: TextStyle(
                                                fontWeight: index==cur_model_select?FontWeight.bold:FontWeight.w100,
                                            )
                                        )
                                      ],
                                    ),
                                    // рисуем список автомобилей
                                        AnimatedOpacity(
                                          opacity: index==cur_model_select ? 1.0 : 0.0,
                                          duration: const Duration(milliseconds: 1500),
                                          child:
                                          ListView.builder(
                                              shrinkWrap: true,
                                              physics: NeverScrollableScrollPhysics(),
                                              padding: const EdgeInsets.all(0),
                                              itemCount: FilteredListAutoes[index]["cars"].length,
                                              itemBuilder: (BuildContext context, int index2) {
                                                return
                                                  Visibility(
                                                    visible: index==cur_model_select?true:false,
                                                    child:
                                                        Padding(
                                                            padding: const EdgeInsets.only(left: 20,top: 4),
                                                            child:
                                                            GestureDetector(
                                                              onTap: () {
                                                                print("тыкнули по автомобилю.."+index2.toString());
                                                              },
                                                              child: Text(FilteredListAutoes[index]["cars"][index2]["name"]),
                                                            )
                                                        )
                                                  );
                                              }
                                          ),
                                    ),
                                    new Divider()
                                  ]
                              )
                          );
                        }
                    ),
                  )
              ),
              Visibility(
                  visible: !auto_list_is_loading,
                  child:
                  Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: const [
                        CardLoading(
                          height: 30,
                          borderRadius: BorderRadius.all(Radius.circular(15)),
                          width: 100,
                          margin: EdgeInsets.only(bottom: 10),
                        ),
                        CardLoading(
                          height: 100,
                          borderRadius: BorderRadius.all(Radius.circular(15)),
                          margin: EdgeInsets.only(bottom: 10),
                        ),
                      ]
                  )
              ),
            ],
          )
        )
    );
  }
}
1 51 52 53 54 55 308